JAVA Floating Point Number’s Rounding Problem & Solution

Photo by Alexander Sinn on Unsplash

What data type are you currently using in Java to perform sensitive floating-point calculations, such as currency? If your answer is float or double, you should read this entire post, which describes the problem with floating-point calculations and offers solutions as well.

Code snippet 1

Let’s take a look at the code above. What is the expected output?

Now simply compile and run the code and see what the actual output is.

Weird? Let's see another test case

Code snippet 2

How many iterations do you think the above code would go through? 10 right? below is our expected output.

When we compile and run the above code, however, it does not stop with 10 iterations. It will continue to loop indefinitely, resulting in a never-ending loop. Furthermore, it prints a value that differs from what we expected. Below is a portion of the actual output of the above program.

The reason for the above problem is Java follows the IEEE 754 Standard to store floating-point numbers. According to IEEE 754 standard, it rounds the value If certain conditions are met. So, if it rounds a value, it means it’s storing a different value instead of the original, right?. As a result, when performing floating-point calculations, java sometimes returns slightly different results. So, let’s take a deep look at how floating-point calculations are performed using the IEEE 754 standard.

IEEE 754 Standard

The IEEE 754 is a technical standard for floating-point computation. IEEE 754 has 3 basic components:

  1. Sign bit: The sign bit will always be the first (and only one) bit. This is as simple as the name suggests. A positive number is represented by 0 and a negative number is represented by 1.
  2. Exponent: This is used to store the exponent value in scientific notation.
  3. Mantissa: The mantissa is part of a number in a scientific notation that contains significant bits.
IEEE-754 format
Single Precision IEEE-754 Floating point standard

For easier understanding let’s convert 9.1 into IEEE 754 format.

⭐️ Step 1️: Separate the whole number and decimal part number

⭐️ Step 2: Convert the whole number into binary

conversion of whole number to binary

⭐️ Step 3: Convert the decimal portion into binary

Conversion of decimal point to binary

Therefore the binary representation of 9.1 is

The binary representation of 9.1

⭐️ step 4: Convert the binary number into base 2 scientific notation

Scientific notation of the binary

⭐️ Step 5: find the value for the exponent bits

For single precision, the exponent bias is 127, which means that to determine the value of the exponent bits, we must first add the base 2 exponent founded previously (3) with the exponent bias (127). As a result, the exponent is 127+3, which equals 130. Then we should convert 130 to binary

⭐️ Step 6: Determine the Mantissa

Determine the mantissa

we won't consider the “1” on the most left side (The number before the decimal point) since every value has that “1”.so we simply copy the decimal portion of the number as mantissa.

⭐️ Step 7: Rounding

In IEEE 754 there is a rule, When calculating the mantissa, if the value is greater than 23 bits, the 24th bit is checked. If the 24th bit is “1,” the rule states that we must add “1” to the 23rd bit to round the value. so in the above example since the value is larger than the 23 bit and since the value of the 24th bit is “1” we must add an additional “1” in 23rd bit.

Rounding the mantissa

Therefore, Finally, IEEE 754 representation of 9.1 is

IEEE 754 representation for 9.1

Therefore above is how the computer stores floating points.

Now let's convert this IEEE 754 representation into decimal back to check whether it gives 9.1

Decimal representation

Therefore, when we store 9.1 computer store it as IEEE 754 standard and since the rounding happening in IEEE 754 format, it will provide a different value which is 9.10000038147 while doing calculations. This is the reason why we got unexpected values earlier on those test cases. so as a conclusion we can say we must not use float or double for the sensitive data’s calculation such as currency.

What is the solution? how can we do floating-point calculation accurately?

The answer is using the appropriate data type. Instead of float or double, we can use BigDecimal class. This class provides operation for arithmetic, comparison, hashing, rounding, manipulation and format conversion. With great precision, this class can handle both very small and very large floating-point numbers.

Now Let's consider how can we solve above-mentioned problems using BigDecimal

problem 1:

Solution for problem 1 above

Problem 2:

Solution for problem 2 above

As conclusion, we must use appropriate data types for the operations.

Keep Learning ❤️

References

2020. How Computer deal with Floating point numbers | Decimal to IEEE 754 Floating point Representation. [video] Available at: <https://www.youtube.com/watch?v=2VM028vpguU&t=7s> [Accessed 7 May 2021].

Wikihow.com. 2021. How to Convert a Number from Decimal to IEEE 754 Floating Point Representation. [online] Available at: <https://www.wikihow.com/Convert-a-Number-from-Decimal-to-IEEE-754-Floating-Point-Representation> [Accessed 7 May 2021].

www.javatpoint.com. 2021. Java BigDecimal — Javatpoint. [online] Available at: <https://www.javatpoint.com/java-bigdecimal#:~:text=The%20BigDecimal%20class%20provides%20operation,a%2032%2Dbit%20integer%20scale.> [Accessed 7 May 2021].

Associate Software Engineer at Virtusa