Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this Java double-parsing behavior according to spec?

The java.lang.Double.parseValue method handles strangely-crafted representations of doubles in an inconsistent way.

If you write a very large number, so large that it is outside of double's range, but then append a large negative exponent to bring it back in range, you end up in range (illustrated here in Scala's REPL):

scala>
java.lang.Double.parseDouble("10000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000001e-400")
res25: Double = 1.0E-21

On the other hand, if you write a very tiny number, so small that it is outside of double's range, but then use a large positive exponent to bring it back in range, it only works if the exponent itself is not too large:

scala> 
java.lang.Double.parseDouble("0.000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000001e400")
res26: Double = Infinity

scala>
java.lang.Double.parseDouble("0.000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000001e200")
res27: Double = 1.0E-179

Is this simply a bug, or is there a spec somewhere that allows this behavior, or are all of these allowed by spec to fail and one should just thank one's blessings when one gets the correct result? (If it's a bug, has it been fixed?)

(Aside: I was writing custom String-to-double code and was going to defer to the Java default implementation for tricky cases, but this test case failed.)

like image 958
Rex Kerr Avatar asked May 18 '14 15:05

Rex Kerr


People also ask

How do you check if a string can be parsed to double?

To check if the string contains numbers only, in the try block, we use Double 's parseDouble() method to convert the string to a Double . If it throws an error (i.e. NumberFormatException error), it means the string isn't a number and numeric is set to false . Else, it's a number.

What is parse double in Java?

The parseDouble() method of Java Double class is a built in method in Java that returns a new double initialized to the value represented by the specified String, as done by the valueOf method of class Double. Syntax: public static double parseDouble(String s)

What is the method used for parsing a value into a double?

parseDouble() We can parse String to double using parseDouble() method.

Is double A class in Java?

Double class is a wrapper class for the primitive type double which contains several methods to effectively deal with a double value like converting it to a string representation, and vice-versa. An object of Double class can hold a single double value.


3 Answers

I think its an edge case, but also a bug. A simpler example is

String text = "0.000000000000000001e326";
System.out.println(Double.parseDouble(text));
System.out.println(new BigDecimal(text).doubleValue());

which prints in Java 7 update 25 and Java 8 update 5

Infinity
1.0E308

The BigDecimal parsing and convert to double shows this number is representable.

like image 200
Peter Lawrey Avatar answered Oct 07 '22 23:10

Peter Lawrey


It's almost certainly not in the spec. The corresponding section about Floating-Point Literals in the JLS only specifies the values of the floating point literals. But it does not speak about valid representations of them.

Of course, there have to be limits. No one would expect that a string like

String s = "0.00... (3 billion zeros) ...001e3000000000";

to be parsed to 1.0. But obviously, the limits are much lower here.

This example shows the limit:

public class DoubleTest
{
    public static void main(String[] args)
    {
        runTest(300, 324);
        runTest(300, 325);
        runTest(300, 326);
    }

    private static void runTest(int negativeExponent, int exponent)
    {
        String s = prefix(negativeExponent)+"1e"+exponent+"D";
        double d = Double.parseDouble(s);
        System.out.println(
            "For 1e-"+negativeExponent+" * 1e"+exponent+" result is "+d);
    }

    private static String prefix(int negativeExponent)
    {
        StringBuilder sb = new StringBuilder("0.");
        for (int i=0; i<negativeExponent; i++)
        {
            sb.append("0");
        }
        return sb.toString();
    }
}

It prints

For 1e-300 * 1e324 result is 9.999999999999999E22

For 1e-300 * 1e325 result is 1.0E24

For 1e-300 * 1e326 result is Infinity

In fact, it is primarily related to the exponent being used. The relevant part that causes this bailout is in FloatingDecimal.java, line 1996.

like image 41
Marco13 Avatar answered Oct 08 '22 00:10

Marco13


It is a bug, but looking at the implementation Oracle might say it's as designed.

The way it's implemented is that the leading kDigits are converted to a long int. As such, in the first example, the calculated exponent is within the Double range and it happily returns the result if it falls within the range.

For the second case, it'd figure the exponent is greater than the maximum decimal exponent and return infinity.

For the third case, it'd reach here and returns the expected result.

Although the links above point to source for OpenJDK 6, it's unlikely that they would have touched the source in question for JDK 7 and 8.

Parsing doubles has traditionally been fun. No surprises at the oddities in this case.

like image 9
devnull Avatar answered Oct 07 '22 22:10

devnull