Вопрос-Ответ

Python rounding error with float numbers [duplicate]

Ошибка округления Python с числами с плавающей точкой [дубликат]

Я не знаю, является ли это очевидной ошибкой, но при запуске скрипта Python для изменения параметров моделирования я понял, что результаты с delta = 0.29 и delta = 0.58 отсутствуют. При расследовании я заметил, что следующий код Python:

for i_delta in range(0, 101, 1):
delta = float(i_delta) / 100

(...)

filename = 'foo' + str(int(delta * 100)) + '.dat'

сгенерированы идентичные файлы для delta = 0.28 и 0.29, то же самое для .57 и .58, причина в том, что python возвращает float(29) / 100 как 0.289999999999998. Но это не систематическая ошибка, не в том смысле, что это происходит с каждым целым числом. Итак, я создал следующий скрипт на Python:

import sys

n = int(sys.argv[1])

for i in range(0, n + 1):
a = int(100 * (float(i) / 100))
if i != a: print i, a

И я не вижу никакой закономерности в числах, для которых возникает эта ошибка округления. Почему это происходит с этими конкретными числами?

Переведено автоматически
Ответ 1

Любое число, которое не может быть построено из точных степеней двойки, не может быть представлено точно как число с плавающей запятой; его необходимо аппроксимировать. Иногда максимальное приближение будет меньше фактического числа.

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

Ответ 2

Она очень хорошо известна из-за природы чисел с плавающей запятой.

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

Например.,

>>> from decimal import Decimal
>>> Decimal(29)/Decimal(100)
Decimal('0.29')
>>> Decimal('0.29')*100
Decimal('29')
>>> int(Decimal('29'))
29

В общем случае десятичная система счисления, вероятно, перегибает палку и по-прежнему будет иметь ошибки округления в редких случаях, когда число не имеет конечного десятичного представления (например, любая дробь, где знаменатель не равен 1 или не делится на 2 или 5 - множители десятичной базы (10)). Например:

>>> s = Decimal(7)
>>> Decimal(1)/s/s/s/s/s/s/s*s*s*s*s*s*s*s
Decimal('0.9999999999999999999999999996')
>>> int(Decimal('0.9999999999999999999999999996'))
0

Поэтому лучше всегда просто округлять перед преобразованием чисел с плавающей точкой в целые числа, если только вам не нужна функция floor.

>>> int(1.9999)
1
>>> int(round(1.999))
2

Другой альтернативой является использование класса Fraction из библиотеки fraction, который не является аппроксимирующим. (Он просто продолжает добавлять / вычитать и умножать целые числители и знаменатели по мере необходимости).

python