Java Doesn’t Print The Shortest Strings That Round-Trip

I’ve always thought Java was one of the languages that prints the shortest decimal strings that round-trip back to floating-point. I was wrong.

In my article “The Shortest Decimal String That Round-Trips May Not Be The Nearest” I describe some shortest string conversions that are tricky to get right. I tried some in Java and guess what? It got them wrong.

After some research I realized the problem is not limited to the tricky power-of-two conversions described in my article; Java fails to print the shortest strings for many other numbers. This is apparently not a bug, because Java is working as designed.

A Quick Test That Proves Java Prints The Shortest Strings?

I had once written this small program to test if Java printed shortest strings:

public class ShortestTest1{
     public static void main(String []args){
        double d = 0;
        for (int i = 1; i <= 10; i++) {
           d += 0.1;
           System.out.println(d);
        }
     }
}

It gave me the expected output:

0.1
0.2
0.30000000000000004
0.4
0.5
0.6
0.7
0.7999999999999999
0.8999999999999999
0.9999999999999999

The floating-point numbers represented by the long strings are printed that way because no shorter strings (e.g., 0.3, 0.8, 0.9, and 1.0) will round-trip. Here are the full-length decimal strings representing the double d after each iteration:

0.1000000000000000055511151231257827021181583404541015625
0.200000000000000011102230246251565404236316680908203125
0.3000000000000000444089209850062616169452667236328125
0.40000000000000002220446049250313080847263336181640625
0.5
0.59999999999999997779553950749686919152736663818359375
0.6999999999999999555910790149937383830547332763671875
0.79999999999999993338661852249060757458209991455078125
0.899999999999999911182158029987476766109466552734375
0.99999999999999988897769753748434595763683319091796875

You can verify that no shorter strings than the ones Java prints will round-trip by using my decimal to floating-point converter.

Aside: These Are Not The Representations of 0.1, 0.2, …, 0.9, 1.0

The values in the Java program are calculated values of 0.1, 0.2, …, 0.9, 1.0, not conversions of them. The conversions are

0.1000000000000000055511151231257827021181583404541015625
0.200000000000000011102230246251565404236316680908203125
0.299999999999999988897769753748434595763683319091796875
0.40000000000000002220446049250313080847263336181640625
0.5
0.59999999999999997779553950749686919152736663818359375
0.6999999999999999555910790149937383830547332763671875
0.8000000000000000444089209850062616169452667236328125
0.90000000000000002220446049250313080847263336181640625
1.0

The conversions all round-trip when shortened to one digit (because of round-tripping in the other direction, where all decimal numbers 15 digits or less round-trip through floating-point).

Testing Problematic Powers of Two

This program tests one of the problematic powers of two from my article, 2-44:

public class ShortestTest2{
     public static void main(String []args){
        double d;
        d = 0.00000000000005684341886080801486968994140625; //2^-44
        System.out.println(d);
     }
}

This prints the 17-digit value 5.6843418860808015E-14, not the shortest, non-nearest, 16-digit expected value, 5.684341886080802E-14.

(Other problematic powers of two fail as well.)

It’s Not Just The Problematic Powers of Two

I had assumed Java printed shortest strings, and decided to look for a statement to that effect in the specification. The closest thing I found was this paragraph in the documentation for the toString() method of class Double (search within the page for the second occurrence of ‘toString(double d)’):

How many digits must be printed for the fractional part of m or a? There must be at least one digit to represent the fractional part, and beyond that as many, but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of type double.

This seems to say Java prints shortest strings. But then I found this old bug report (written in 2001, still open): “Double.toString(double) sometimes produces incorrect results”. (Sometimes that link doesn’t work; try the OpenJDK copy instead.)

In this bug report are:

  • Other examples of non-shortest conversions.
  • An explanation as to why the Java implementation gets it wrong.
  • A link (which unfortunately is dead) to code that would fix the problem.
  • Examples of other printing anomalies, like not choosing the nearer of two numbers that round-trip, and printing a number to 18 digits (only up to 17 is ever necessary!)
  • A response that explains that Java is working as designed.

It is a very good read.

Dingbat

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

(Cookies must be enabled to leave a comment...it reduces spam.)

Copyright © 2008-2024 Exploring Binary

Privacy policy

Powered by WordPress

css.php