PHP Exception Handling - ErrorException

PHP Exception Handling – UnderflowException

Making our way through our in-depth PHP Exception Handling series, today we’ll be going over the UnderflowException. If you’re familiar with computer-based arithmetic rules, you’ll likely be able to deduce that the UnderflowException is intended when performing arithmetic using decimal numbers that result in an inaccurate value, because the PHP engine cannot represent the proper scale of the actual, absolute value.

In this article we’ll explore the UnderflowException in more detail, starting with where it sits in the larger PHP Exception Hierarchy. We’ll also examine a fully functional PHP code sample illustrating the basic process of determining how accurate your own PHP installation is when it comes to decimal scale, and how to use that information to throw UnderflowExceptions where appropriate. Let’s get started!

The Technical Rundown

All PHP errors implement the Throwable interface, or are extended from another inherited class therein. The full exception hierarchy of this error is:

Full Code Sample

Below is the full code sample we’ll be using in this article. Feel free to use any or all of the code if you wish to follow along.

When Should You Use It?

As mentioned in the introduction, arithmetic underflow indicates that the result of a calculation is a smaller absolute value than the system can accurately store. This can be for a variety of reasons, but typically underflow occurs when performing operations with large decimal numbers. The precision of such a number indicates the total number of digits (i.e. length), while the scale indicates the number of digits following the decimal point. For example, 24.601 has a precision of 5 and a scale of 3.

Most programming languages, operating systems, CPUs, and so forth have a maximum amount of memory that can be devoted to storing a single value. Thus, there must be a limit to the total accuracy (precision and/or scale) that can be stored for a decimal. When the calculating engine cannot hold a decimal value that exceeds those limits, that number is typically converted to a floating point value. This conversion is where problems can arise, which may lead to UnderflowExceptions in the case of PHP. As we’ll see in the example code in just a moment, PHP can only represent decimal values with a relatively small scale limit, and once that scale value is exceeded (i.e. the absolute value is too infinitesimal), PHP represents the value with a slightly inaccurate floating point representation.

These imprecise numeric values are usually insignificant, but in the case of strict application domains, such as banking or security, it can be critical that there is no chance for data loss or inaccuracy when handling particularly big or small numbers.

To illustrate these situations we start with the isScaleAccurate($scale, $debugOutput = false) function:

This function creates a string in the form of 0.999...n with a total scale equal to the passed $scale parameter, then converts that value to a float. By subtracting that value from 1, an accurate $result would be a decimal in the form of 0.000...1. However, PHP cannot handle decimals with very large scales, so if the $result is converted to a float it will be in the form of 9.999...8E-n. No matter the actual floating point value, such conversion always indicates a slight loss in accuracy compared to the aforementioned decimal form. Therefore, we can check the form of the $result to see if there is accuracy loss or not, given the passed $scale parameter.

We make use of the isScaleAccurate($scale, $debugOutput = false) function inside getScaleAccuracy($debugOutput = false):

This function retrieves the highest scale value that remains accurate within the current PHP engine. With this maximum scale value in hand, we can use it to properly throw an UnderflowException throughout our custom code, wherever appropriate. For example, here we have the addNumbers($a, $b) function, that adds two numbers:

It’s critical that our addNumbers($a, $b) function only produces the precisely accurate results. Thus, we use the getScale($number) helper function, along with getScaleAccuracy(), to determine if the two passed $a and $b parameters contain a scale value that exceeds the maximum scale accuracy our current PHP engine can handle. If so, we throw a new UnderflowException indicating as much, otherwise we perform the calculation and output the result to the log.

To test this out we start by calling getScaleAccuracy($debugOutput = false) to display the scale accuracy output of the current PHP engine:

As we can see from the output, my current PHP maxes out at a scale value of 3, after which it produces inaccurate floating point values. Thus, we’ll test the addNumbers($a, $b) function by passing in a series of increasingly-precise decimals:

Executing the above tests produces the following output:

As expected, we’re able to perform totally accurate calculations up until we exceed a scale maximum of 3. Trying to use values with scale of 4+ produces an UnderflowException, indicating the issue to the user.

Check out the Airbrake-PHP library, designed to quickly and easily integrate into any PHP project, giving you and your team access to real-time error monitoring and reporting throughout your application’s entire life cycle. With automatic, instantaneous error and exception notifications at your fingertips, you’ll be constantly aware of your application’s health, including any issues that may arise. Best of all, with Airbrake’s robust web dashboard cataloging every error that occurs, you and your team can immediately dive into the exact details of what went wrong, making it easy to quickly recognize and resolve problems.