这是根据规范的Java双解析行为吗?

Rex*_*err 23 java double parsing number-formatting

java.lang.Double.parseValue方法以不一致的方式处理奇怪的双打表示.

如果你写了一个非常大的数字,那么它超出了double范围,但是然后附加一个大的负指数使它回到范围内,你最终在范围内(这里用Scala的REPL说明):

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

另一方面,如果你写一个非常小的数字,这么小,它超出了double范围,但是然后使用一个大的正指数将它带回范围内,它只有在指数本身不是太大时才有效:

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
Run Code Online (Sandbox Code Playgroud)

这只是一个错误,或者某个地方是否存在允许这种行为的规范,或者规范允许所有这些都失败了,当得到正确的结果时,应该感谢一个人的祝福?(如果它是一个错误,它已被修复?)

(旁白:我正在编写自定义的字符串到双重代码,并且会针对棘手案例推迟Java默认实现,但此测试用例失败.)

Pet*_*rey 14

我认为它是一个边缘案例,但也是一个错误.一个更简单的例子是

String text = "0.000000000000000001e326";
System.out.println(Double.parseDouble(text));
System.out.println(new BigDecimal(text).doubleValue());
Run Code Online (Sandbox Code Playgroud)

在Java 7 update 25和Java 8 update 5中打印

Infinity
1.0E308
Run Code Online (Sandbox Code Playgroud)

BigDecimal解析并转换为double表示此数字是可表示的.


Mar*_*o13 10

这几乎肯定不符合规范.JLS中有关浮点文字的相应部分仅指定浮点文字的.但它并没有谈论它们的有效表述.

当然,必须有限制.没有人会想到像这样的字符串

String s = "0.00... (3 billion zeros) ...001e3000000000";
Run Code Online (Sandbox Code Playgroud)

被解析为1.0.但显然,这里的限制要低得多.

此示例显示了限制:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

它打印

对于1e-300*1e324,结果为9.999999999999999E22

对于1e-300*1e325,结果是1.0E24

对于1e-300*1e326,结果是Infinity

实际上,它主要与使用的指数有关.导致这种救助的相关部分是在1996年的FloatingDecimal.java中.


dev*_*ull 9

这是一个错误,但看看Oracle的实现可能会说它是按照设计的.

该方法它的实施是主导kDigits转换为一个长整型.因此,在第一个例子中,计算出的指数在该Double范围内,如果它落在该范围内,它会愉快地返回结果.

对于第二种情况,它表示指数大于最大小数指数并返回无穷大.

对于第三种情况,它会到达此处并返回预期结果.

虽然上面的链接指向OpenJDK 6的源代码,但它们不太可能触及 JDK 7和8的相关源.

解析双打传统上很有趣.在这种情况下奇怪的事情没有惊喜.