Подтвердить что ты не робот

Ошибка округления Python с поплавковыми номерами

Я не знаю, является ли это очевидной ошибкой, но при запуске Python script для изменения параметров моделирования я понял, что результаты с 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.28999999999999998. Но это не систематическая ошибка, а не то, что происходит с каждым целым числом. Поэтому я создал следующий Python script:

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

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

4b9b3361

Ответ 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

Так что лучше всего всегда округлить до того, как вы будете плавать с плавающей точкой в ​​ints, если вы не хотите использовать функцию floor.

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

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