Подтвердить что ты не робот

Является ли это поведение двойного разбора Java по спецификации?

Метод java.lang.Double.parseValue обрабатывает странно созданные представления двойников несогласованным способом.

Если вы пишете очень большое число, настолько большое, что оно находится за пределами диапазона double, но затем добавьте большой отрицательный показатель, чтобы вернуть его в диапазон, вы попадаете в диапазон (см. здесь в Scala REPL ):

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

С другой стороны, если вы пишете очень маленькое число, настолько маленькое, что оно находится вне диапазона 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

Является ли это просто ошибкой или есть спецификация где-то, что допускает такое поведение, или все это разрешено с помощью спецификации, и нужно просто благодарить одно благословение, когда вы получаете правильный результат? (Если это ошибка, исправлена ​​ли она?)

(Помимо этого: я писал собственный код String-to-double и собирался отложить выполнение Java по умолчанию для сложных случаев, но этот тестовый пример не удалось.)

4b9b3361

Ответ 1

Это почти наверняка не в спецификации. Соответствующий раздел о Литералах с плавающей запятой в JLS определяет только значения литералов с плавающей запятой. Но он не говорит о действительных представлениях о них.

Конечно, должны быть ограничения. Никто не ожидал, что строка типа

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

для синтаксического анализа на 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();
    }
}

Он печатает

Результат 1e-300 * 1e324 - 9.999999999999999E22

Для результата 1e-300 * 1e325 1.0E24

Для результата 1e-300 * 1e326 бесконечность

Фактически, это прежде всего связано с используемым показателем. Соответствующая часть, которая вызывает эту помощь, находится в FloatingDecimal.java, строка 1996.

Ответ 2

Я думаю, что это край, но и ошибка. Более простой пример:

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

который печатает в обновлении Java 7 25 и обновлении Java 8 5

Infinity
1.0E308

Анализ BigDecimal и преобразование в double показывают, что это число является представимым.

Ответ 3

Это ошибка, но, глядя на реализацию, Oracle может сказать, что она сконструирована.

Способ реализованный, заключается в том, что ведущие kDigits преобразуются в длинный int. Таким образом, в первом примере вычисляемый показатель находится в пределах диапазона Double, и он с радостью возвращает результат, если он попадает в диапазон.

Во втором случае показатель экспоненты больше, чем максимальный десятичный показатель и возвращает бесконечность.

В третьем случае он достигнет здесь и вернет ожидаемый результат.

Хотя ссылки выше указывают на источник для OpenJDK 6, маловероятно, чтобы они коснулись источника, о котором идет речь, для JDK 7 и 8.

Парсинг удваивается традиционно весело. В этом случае сюрпризов нет.