Добавление и вычитание удвоений дает странные результаты
Итак, когда я добавляю или вычитаю в Java с парными, это дает мне странные результаты. Вот некоторые из них:
Если я добавлю 0.0 + 5.1
, он даст мне 5.1
. Это правильно.
Если я добавлю 5.1 + 0.1
, он даст мне 5.199999999999
(число повторений 9
может быть выключено). Это неправильно.
Если я вычитаю 4.8 - 0.4
, он дает мне 4.39999999999995
(опять же, повторяющийся 9
может быть выключен). Это неправильно.
Сначала я думал, что это только проблема с добавлением удвоений с десятичными значениями, но я был неправ. Следующие работали нормально:
5.1 + 0.2 = 5.3
5.1 - 0.3 = 4.8
Теперь первое добавленное число - это double, сохраненный как переменная, хотя вторая переменная захватывает текст из JTextField
. Например:
//doubleNum = 5.1 RIGHT HERE
//The textfield has only a "0.1" in it.
doubleNum += Double.parseDouble(textField.getText());
//doubleNum = 5.199999999999999
Ответы
Ответ 1
В Java значения double
числа с плавающей точкой IEEE. Если они не являются степенью 2 (или суммой степеней 2, например 1/8 + 1/4 = 3/8), они не могут быть представлены точно, даже если они имеют высокую точность. Некоторые операции с плавающей запятой объединяют ошибку округления, присутствующую в этих числах с плавающей запятой. В описанных выше случаях ошибки с плавающей запятой стали достаточно значительными, чтобы отображаться на выходе.
Не имеет значения, является ли источник номера, анализирует ли строка из JTextField
или задает литерал double
- проблема наследуется в представлении с плавающей запятой.
Обходные:
-
Если вы знаете, что у вас будет только столько десятичных точек, тогда используйте целое число
арифметику, затем преобразовать в десятичную цифру:
(double) (51 + 1) / 10
(double) (48 - 4) / 10
-
Используйте BigDecimal
-
Если вы должны использовать double
, вы можете сократить число ошибок с плавающей запятой
с алгоритмом суммирования Кахана.
Ответ 2
В Java удваивает использование арифметики с плавающей запятой IEEE 754 (см. this Статья в Википедии), которая по своей сути неточна. Используйте BigDecimal для идеальной десятичной точности. Чтобы округлить печать, принимая только "довольно хорошую" точность, используйте printf("%.3f", x)
.