Странное поведение с плавающей запятой в программе на Java

В моей программе есть один массив с 25 двойными значениями 0,04 Когда я пытаюсь суммировать эти значения в цикле, я получаю следующие результаты:

0.0 + 0.04 = 0.04
0.04 + 0.04 = 0.08
0.08 + 0.04 = 0.12
0.12 + 0.04 = 0.16
0.16 + 0.04 = 0.2
0.2 + 0.04 = 0.24000000000000002
0.24000000000000002 + 0.04 = 0.28
0.28 + 0.04 = 0.32
0.32 + 0.04 = 0.36
0.36 + 0.04 = 0.39999999999999997
0.39999999999999997 + 0.04 = 0.43999999999999995
0.43999999999999995 + 0.04 = 0.4799999999999999
0.4799999999999999 + 0.04 = 0.5199999999999999
0.5199999999999999 + 0.04 = 0.5599999999999999
0.5599999999999999 + 0.04 = 0.6
0.6 + 0.04 = 0.64
0.64 + 0.04 = 0.68
0.68 + 0.04 = 0.7200000000000001
0.7200000000000001 + 0.04 = 0.7600000000000001
0.7600000000000001 + 0.04 = 0.8000000000000002
0.8000000000000002 + 0.04 = 0.8400000000000002
0.8400000000000002 + 0.04 = 0.8800000000000002
0.8800000000000002 + 0.04 = 0.9200000000000003
0.9200000000000003 + 0.04 = 0.9600000000000003

Какого черта такое могло случиться ?!

В некотором роде вопрос: stackoverflow.com/questions/327020/…

CesarB 29.11.2008 17:36
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
11
1
2 829
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Наиболее распространенное хранилище значений с плавающей запятой в языках программирования - IEEE одиночные и парные разряды - не имеет точного представления для большинства десятичных дробей.

Причина в том, что они хранят значения в двоичном формате с плавающей запятой, а не в десятичном формате с плавающей запятой. Единственные дробные значения, которые могут быть представлены точно, - это суммы отрицательных степеней двойки. Цифры вроде:

  • 0,5 (2 ^ -1)
  • 0,125 (2 ^ -3)
  • 0,625 (2 ^ -1 + 2 ^ -3)

И т.п.

То, что вы видите, - это тот факт, что представления чисел, таких как 0,96, не могут быть точно представлены, потому что они не могут быть выражены как сумма отрицательных степеней двойки. Таким образом, при распечатке с полной точностью в виде десятичной дроби они не будут соответствовать исходному значению.

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

jalf 29.11.2008 17:59

@jalf, правда, но хотя бы десятичное представление с плавающей запятой удивило бы меньше людей, привыкших к простому карманному учету. Сложить стопку в 20 центов и не получить ровно один доллар - это удивительно для многих. Десятичная система счисления поможет в этом случае правильно и заменит ее на забавные проблемы с числами, которые должны быть знакомы с уроков арифметики в начальной школе.

RBerteig 13.06.2009 03:27

См. Также «Что должен знать каждый компьютерный ученый о плавающей запятой»

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

Barry Kelly 29.11.2008 16:53

Я согласен. С другой стороны, практикам-самоучкам и отличникам приятно знать, где он находится, на случай, если они столкнутся с числовыми проблемами.

John with waffle 29.11.2008 16:58

Это все еще то, что люди должны знать, даже если он слишком сложен для чтения. ;)

jalf 29.11.2008 19:57

В других ответах упоминалось, почему, но не как этого избежать.

Есть несколько решений:

  • Масштабирование: если все ваши числа кратны 0,01 (например), умножьте все на 100 и используйте целочисленную арифметику (которая является точной).
  • Числовой тип: если ваш язык имеет числовой тип (например, тип numeric в SQL), вы можете его использовать.
  • Рациональные числа произвольной точности: используйте библиотеку bignum, такую ​​как GMP, которая позволяет вам представлять эти числа как отношение двух целых чисел.
  • Десятичное число с плавающей запятой: если у вас есть десятичное число с плавающей запятой, такое как в IEEE-754r, вы можете его использовать.

Вы можете попробовать класс java BigDecimal в качестве альтернативы float и double.

Другие вопросы по теме