Изменение порядка в сумме Операция может дать разные результаты?
1. обзор
В этой быстрой статье мы рассмотрим, почему изменение порядка сумм дает другой результат.
2. проблема
Когда мы смотрим на следующий код, мы можем легко предсказать правильный ответ (13.22 + 4.88 + 21.45 = 39.55). То, что легко для нас, может быть по-разному интерпретировано компилятором Java:
double a = 13.22;
double b = 4.88;
double c = 21.45;
double abc = a + b + c;
System.out.println("a + b + c = " + abc); // Outputs: a + b + c = 39.55
double acb = a + c + b;
System.out.println("a + c + b = " + acb); // Outputs: a + c + b = 39.550000000000004
С математической точки зрения изменение порядка сумм должно всегда давать один и тот же результат:
(A + B) + C = (A + C) + B
Это верно и хорошо работает в Java (и других языках программирования) для целых чисел. Однако почти все процессоры используют для нецелых чиселIEEE 754 binary floating point standard, что приводит к неточности при сохранении десятичного числа как двоичного значения. Компьютеры не могут точно представить все действительные числа.
Когда мы меняем порядок, мы также меняем промежуточное значение, которое хранится в памяти, и, следовательно, результат может отличаться. В следующем примере мы просто начнем с суммы A + B или A + C:
double ab = 18.1; // = 13.22 + 4.88
double ac = 34.67; // = 13.22 + 21.45
double sum_ab_c = ab + c;
double sum_ac_b = ac + b;
System.out.println("ab + c = " + sum_ab_c); // Outputs: 39.55
System.out.println("ac + b = " + sum_ac_b); // Outputs: 39.550000000000004
3. Решение
Из-за печально известной неточности чисел с плавающей запятой, double никогда не должен использоваться для точных значений. Это включает в себя валюту. Для точных значений мы можем использовать классBigDecimal:
BigDecimal d = new BigDecimal(String.valueOf(a));
BigDecimal e = new BigDecimal(String.valueOf(b));
BigDecimal f = new BigDecimal(String.valueOf(c));
BigDecimal def = d.add(e).add(f);
BigDecimal dfe = d.add(f).add(e);
System.out.println("d + e + f = " + def); // Outputs: 39.55
System.out.println("d + f + e = " + dfe); // Outputs: 39.55
Теперь мы видим, что в обоих случаях результаты были одинаковыми.
4. Заключение
При работе с десятичными значениями мы всегда должны помнить, что числа с плавающей запятой представлены неправильно, и это может привести к неожиданным и нежелательным результатам. Когда требуется точность, мы должны использовать классBigDecimal.
Как всегда, код, использованный в статье, находится вover on GitHub.