Java удваивает добавление странным образом (и нет, это не за деньги) - PullRequest
2 голосов
/ 17 августа 2011

Так что это единственный соответствующий раздел кода

System.out.println("first term of " + firstTerm +
                   " second term of " + secondTerm + 
                   " third term of " + finalTermHolder + 
                   " should equal " + oppositeIntHolder);
double holder = firstTerm + secondTerm + finalTermHolder;
System.out.println(holder + " should equal " + oppositeIntHolder);

Это непрерывный код, между ними ничего нет.Вывод для первого println:

first term of 2.5147186257614296 second term of -9.514718625761429 third term of 7.0 should equal 0.0

Второй println приводит к:

8.881784197001252E-16 should equal 0.0

Почему -9,5, 2,5 и 7 складываются до 8,9 вместо 0?

Ответы [ 3 ]

4 голосов
/ 17 августа 2011

они не складываются до 8,9. они добавляют до 8,9e-16. это что-то вроде 0,00000000000000089

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

2 голосов
/ 17 августа 2011

8.881784197001252E-16 намного ближе к нулю, чем вы думаете; )

Double в Java - это число с плавающей запятой. Если вы ищете точное представление числа, попробуйте использовать BigDecimal вместо Double

BigDecimal num1 = new BigDecimal(3.32);
BigDecimal num2 = new BigDecimal(3.68);
System.out.println(num1.add(num2)); //will output 7.0
0 голосов
/ 17 августа 2011

При выполнении двойных операций необходимо обеспечить соответствующее округление.Даже для деления BigDecimal необходимо обеспечить соответствующее округление.

Для печати double выполняется небольшое количество округления, чтобы вы не увидели ошибку представления.Однако, после нескольких вычислений (требуется только один), ошибка округления слишком велика, и вы можете увидеть ошибку.

Если вы хотите увидеть ошибку представления и округления, используйте BigDecimal, поскольку он выполняет точное преобразование издвойной.Что-то, что само по себе может удивить.

Кстати, вы не получите ошибку округления с простыми степенями 2. поэтому -9,5 + 2,5 + 7,0 всегда будет 0,0.Вы получите ошибку округления только с другими десятичными знаками, такими как 0,1

double[] ds = {
        0.1,
        0.2,
        -0.3,
        0.1 + 0.2 - 0.3};
for (double d : ds) {
    System.out.println(d + " => " + new BigDecimal(d));
}

prints

0.1 => 0.1000000000000000055511151231257827021181583404541015625
0.2 => 0.200000000000000011102230246251565404236316680908203125
-0.3 => -0.299999999999999988897769753748434595763683319091796875
5.551115123125783E-17 => 5.5511151231257827021181583404541015625E-17

Вы можете видеть, что представление для 0,1 и 0,2 немного выше этих значений, и -0,3 этотакже немного выше.Когда вы печатаете их, вы получите более приятное значение 0,1 вместо фактического значения, представленного 0,1000000000000000055511151231257827021181583404541015625

Однако, когда вы добавляете эти значения вместе, вы получаете значение, немного превышающее 0.

КомуЧтобы решить эту проблему, необходимо обеспечить соответствующее округление.С деньгами это легко, поскольку вы знаете, сколько десятичных разрядов уместно, и если у вас нет $ 70 триллионов, вы не получите достаточно большую ошибку округления, вы не сможете ее исправить.

public static double roundToTwoPlaces(double d) {
    return ((long) (d < 0 ? d * 100 - 0.5 : d * 100 + 0.5)) / 100.0;
}

Если вы добавите это вВ результате все еще существует небольшая ошибка представления, однако она недостаточно велика, чтобы Double.toString (d) не могла исправить ее.

double[] ds = {
        0.1,
        0.2,
        -0.3,
        0.1 + 0.2 - 0.3};
for (double d : ds) {
    System.out.println(d + " to two places " + roundToTwoPlaces(d) + " => " + new BigDecimal(roundToTwoPlaces(d)));
}

print

0.1 to two places 0.1 => 0.1000000000000000055511151231257827021181583404541015625
0.2 to two places 0.2 => 0.200000000000000011102230246251565404236316680908203125
-0.3 to two places -0.3 => -0.299999999999999988897769753748434595763683319091796875
5.551115123125783E-17 to two places 0.0 => 0
...