Home > Blockchain >  The calculation accuracy of floating-point numbers (float, double) in Java (IEEE 754)
The calculation accuracy of floating-point numbers (float, double) in Java (IEEE 754)

Time:12-19

I tried the following calculations in jshell of JDK 11:

jshell> 0.1   0.2 == 0.3
$1 ==> false

I can understand this return. After all, neither 0.1, 0.2, or 0.3 can be accurately represented in binary.

But when I switched to the float instead of the double, I was surprised to find that the return of jshell was true:

jshell> 0.1f   0.2f == 0.3f
$2 ==> true

This is contrary to my understanding all the time. So I tried to make jshell calculate directly:

jshell> 0.1   0.2
$3 ==> 0.30000000000000004

jshell> 0.1f   0.2f
$4 ==> 0.3

Indeed, if I use the float data type, it seems to be able to accurately calculate the result to be 0.3.

But why? If double can't accurately represent and calculate 0.1 0.2, then why float can?

If there is any error in my test, please point it out. Thank you first!

CodePudding user response:

Even as a float, the number isn't actually 0.3.

    public static void main (String[] args) throws java.lang.Exception
    {
        float a = 0.1f;
        float b = 0.2f;
        float c = 0.3f;
        float d = a   b;
 
        System.out.println( new BigDecimal(c)   ", "   new BigDecimal(d));
    }

That puts out.

0.300000011920928955078125, 0.300000011920928955078125

You've found a case where the errors are offsetting.

It would be similar to using a decimal system, and adding 1/3 2/3. 1/3 is approximately 0.333 and 2/3 is approximately 0.667 so when you add the two you get 1.0 even though both of them are approximate.

CodePudding user response:

Each time you perform floating-point operation, the ideal real-number-arithmetic result is rounded to the nearest value representable in the floating-point format, using whichever rounding method applies to the operation (most often round-to-nearest-ties-to-even).

Sometimes a rounding will be in the direction that cancels previous roundings. Sometimes a rounding will be in the direction that exacerbates previous roundings.

Converting the source text 0.1 to double is a floating-point operation. It produces 0.1000000000000000055511151231257827021181583404541015625, so rounding made the result bigger.

Converting 0.2 to double produces 0.200000000000000011102230246251565404236316680908203125, again bigger.

Converting 0.3 to double` produces 0.299999999999999988897769753748434595763683319091796875, so rounding made the result smaller.

Adding the first two, 0.1000000000000000055511151231257827021181583404541015625 and 0.200000000000000011102230246251565404236316680908203125, produces 0.3000000000000000444089209850062616169452667236328125. Here rounding again increased the result.

Converting 0.1f to float produces 0.100000001490116119384765625.

Converting 0.2f to float produces 0.20000000298023223876953125.

Converting 0.3f to float produces 0.300000011920928955078125. In this case, it happens that, because fewer numbers are representable in float than in double, the next float value below 0.3 is farther away from 0.3 than 0.300000011920928955078125 is. So converting 0.3f to float rounds up even though converting 0.3 to double rounds down.

Adding the first two of these float values, 0.100000001490116119384765625 and 0.20000000298023223876953125, produces 0.300000011920928955078125. Since that is the same as the result of converting 0.3f, 0.1f 0.2f == 0.3f evaluates as true.

Another thing to note is that Java’s default display of floating-point numbers produces just enough significant digits to uniquely distinguish the value within its floating-point format. This means, when Java shows “0.3” for a number, it does not mean the floating-point value is 0.3. It means the floating-point value is closer to 0.3 than any other value in that format is, so printing “0.3” is enough to identify it.

This means that when “0.3” is printed for a double, the actual double value is 0.299999999999999988897769753748434595763683319091796875, but, when “0.3” is printed for a float, the actual float value is 0.300000011920928955078125. Java is not designed to show you the true values of floating-point numbers.

  • Related