Парсинг двойных значений с использованием Double против BigDecimal в Java - PullRequest
13 голосов
/ 26 сентября 2019

Я опытный разработчик, но не математик.Я достаточно знаю о спецификации IEEE с плавающей запятой, чтобы бояться делать предположения о разборе, печати и сравнении их.

Я знаю, что могу проанализировать double из String, используя Double.parseDouble(String s).Я знаю, что могу также проанализировать эту же строку в BigDecimal, используя new BigDecimal(String s), а затем попросить BigDecimal для double, используя BigDecimal.doubleValue().

Я посмотрел на API и код для обоих методов, и кажется, что BigDecimal имеет много различных вариантов анализа и преобразования.

Это оба метода (Double.parseDouble(s) и new BigDecimal(s).doubleValue())гарантируется, что для всех строковых входов будет получено одинаковое примитивное значение double, при условии, что значение не выходит за пределы плюс или минус Double.MAX_DOUBLE?

Ответы [ 2 ]

4 голосов
/ 26 сентября 2019

Гарантированы ли оба метода (Double.parseDouble(s) и new BigDecimal(s).doubleValue()) для всех строковых входов для получения точно одинакового двойного примитивного значения, если значение не находится вне диапазона плюс или минус Double.MAX_DOUBLE?

Нет, конечно, нет.

С одной стороны, есть некоторые строковые входы, которые Double.parseDouble(s) поддерживает, но new BigDecimal(s) нет (например, шестнадцатеричные литералы).

Для другого Double.parseDouble("-0") дает отрицательный ноль, тогда как new BigDecimal("-0").doubleValue() дает положительный ноль (потому что BigDecimal не имеет понятия со знаком ноль).

И хотя это не имеет прямого отношения к вашему вопросу, меня попросили указать в интересах других читателей, что Double.parseDouble(s) поддерживает NaN и бесконечности, тогда как BigDecimal - нет.

3 голосов
/ 26 сентября 2019

Для большинства входных значений оба метода должны давать одинаковые значения.Хотя все еще возможно, что они не могут, это кажется маловероятным.

Конструктор BigDecimal(String) Javadocs сообщает:

Примечание API:

Для значений, отличных от float и double NaN и ± Infinity, этот конструктор совместим со значениями, возвращаемыми Float.toString(float) и Double.toString(double).

Однако метод Double.parseDouble(String) сообщает:

Возвращает новый double, инициализированный значением, представленным указанным String, в соответствии с valueOfметод класса Double.

И это далее для описания формата, принятого методом.

Давайте проверим!

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

public static void main(String[] args)
{
    String[] values = {"0", "0.1", "0.33333333333333333333", "-0", "-3.14159265", "10.1e100",
            "0.00000000000000000000000000000000000000000000000000142857142857",
            "10000000000.000000000000000001", "2.718281828459",
            "-1.23456789e-123", "9.87654321e+71", "66666666.66666667",
            "1.7976931348623157E308", "2.2250738585072014E-308", "4.9E-324",
            "3.4028234663852886E38", "1.1754943508222875E-38", "1.401298464324817E-45",
            String.valueOf(Math.E), String.valueOf(Math.PI), String.valueOf(Math.sqrt(2))
    };
    for (String value : values) {
        System.out.println(isDoubleEqual(value));
    }
}
// Test if the representations yield the same exact double value.
public static boolean isDoubleEqual(String s) {
    double d1 = Double.parseDouble(s);
    double d2 = new BigDecimal(s).doubleValue();
    return d1 == d2;
}

Для этих значений я получаювсе true с.Это ни в коем случае не является исчерпывающим, поэтому было бы очень трудно доказать это верно для всех возможных double значений.Все, что нужно, это один false, чтобы показать контрпример.Однако это, кажется, является некоторым доказательством того, что это верно для всех допустимых строковых представлений double.

Я также пробовал начальные пробелы, например, " 4".Конструктор BigDecimal(String) выбросил NumberFormatException, но Double.parseDouble правильно обрезал ввод.

Конструктор BigDecimal(String) не примет Infinity или NaN, но вы спрашивали только о нормальном конечномассортимент.Метод Double.parseDouble принимает шестнадцатеричные представления с плавающей запятой, а BigDecimal(String) - нет.

Если вы включите эти крайние случаи, один метод может вызвать исключение, а другой - нет.Если вы ищете обычные строки из 10 с конечными значениями в пределах диапазона, ответ «это кажется вероятным».

...