Skip to content

Safely convert `float` to `double` without loss of precision

I have the following head scratcher, demonstrated in jshell 11.0.12 and 17.0.1 here

jshell> 13.9f
$1 ==> 13.9

jshell> 13.9
$2 ==> 13.9

jshell> (double) 13.9f
$3 ==> 13.899999618530273

The same thing occurs in a simple compiled class in both versions

strictfp public class Test {

    public strictfp static void main(String[] args) {
        System.out.format("%s%n", (double) 13.9f);
        System.out.format("%s%n", 13.9f);
    }

}
╰─➤  java Test      
13.899999618530273
13.9

Although 17 warns that strictfp is no longer required

warning: [strictfp] as of release 17, all floating-point expressions are evaluated strictly and 'strictfp' is not required

The JLS says this in section 5.1.2

A widening primitive conversion does not lose information about the overall
magnitude of a numeric value in the following cases, where the numeric value is
preserved exactly:
• from an integral type to another integral type
• from byte, short, or char to a floating-point type
• from int to double
• from float to double

In 14 it contained the following after the bullet list

A widening primitive conversion from float to double that is not strictfp may
lose information about the overall magnitude of the converted value.

Based on my reading this is a bug in the implementation? The only reliable way I’ve found to perform this conversion is Double.parseDouble(Float.toString(13.9f)).

Answer

Based on my reading this is a bug in the implementation?

No. It’s a bug in your expectations. The double value you’re seeing is exactly the same value as the float value. The precise value is 13.8999996185302734375.

That’s not the same as “the closest double value to 13.9″ which is 13.9000000000000003552713678800500929355621337890625.

You’re assigning the value 13.8999996185302734375 to a double value, and then printing the string representation – which is 13.899999618530273 as that’s enough precision to completely distinguish it from other double values. If it were to print 13.9, that would be a bug, as there’s a double value that’s closer to 13.9, namely 13.9000000000000003552713678800500929355621337890625.