Почему результат кода ниже 0 в python3?
a = "4.15129406851375e+17"
a = float(a)
b = "415129406851375001"
b = float(b)
a-b
Прочитав документацию чего конкретно вы ожидали в качестве другого результата?
@khelwood: это не лучший дубликат; он охватывает «Математика с плавающей запятой на самом деле нарушена (для определенного определения сломанной)», но проблемы здесь связаны с превышением ограничений float
для представления целых чисел, а не с точностью справа от десятичной дроби (даже если две проблемы в некоторой степени связаны).
используйте встроенный пакет decimal
для преобразования ваших строк в decimal.Decimal
, тогда он напечатает правильное значение -1
Проголосовали за повторное открытие: похоже на еще один случай, когда закрытое голосование связано с общей проблемой (о которой ОП может даже знать), но здесь более уместен более точный ответ.
Спасибо @кая! Математика с плавающей точкой не работает? всегда полезная ссылка, а не дубликат (ответы косвенно касаются связанной информации, без подробностей), но я не смог найти дубликаты, характерные для потери точности float
для больших чисел.
Это происходит потому, что и 415129406851375001
, и 4.15129406851375e+17
превышают пределы целочисленного представления C double
(в терминах которого реализован Python float
).
Как правило, C double
представляют собой IEEE 754 64-битные двоичные значения с плавающей запятой, что означает, что они имеют 53-битную целочисленную точность (последние последовательные целочисленные значения, которые float
может представлять, это 2 ** 53 - 1
, за которым следует 2 ** 53
; он не может представлять 2 ** 53 + 1
). Проблема в том, что для хранения 415129406851375001
требуется 59 бит целочисленной точности ((415129406851375001).bit_length()
предоставит эту информацию). Когда значение слишком велико для одной только мантиссы (целого компонента), компонент экспоненты значения с плавающей запятой используется для масштабирования меньшего целочисленного значения в степени 2, чтобы оно было примерно на уровне исходного значения, но это означает что представляемые целые числа начинают пропускать сначала на 2 (поскольку вам требуется> 53 бита), затем на 4 (для> 54 бит), затем на 8 (> 55 бит), затем на 16 (> 56 бит) и т. д., пропуская в два раза больше между представимыми значениями для каждого имеющегося у вас бита величины, который не может быть представлен в 53 битах.
В вашем случае оба числа, преобразованные в float
, имеют целочисленное значение 415129406851374976
(print(int(a), int(b))
покажет вам истинное целочисленное значение; они слишком велики, чтобы иметь какой-либо дробный компонент), потеряв точность в младших цифрах.
Если вам нужна произвольно точная математика с плавающей запятой с основанием 10, замените использование float
на decimal.Decimal (удобно, ваши значения уже являются строками, поэтому вы не рискуете потерять точность между тем, как вы вводите float
и фактическое значение сохранено); точность по умолчанию будет обрабатывать эти значения, и вы можете увеличить ее, если вам нужны большие значения. Если вы сделаете это, вы получите ожидаемое поведение:
from decimal import Decimal as Dec # Import class with shorter name
a = "4.15129406851375e+17"
a = Dec(a) # Convert to Decimal instead of float
b = "415129406851375001"
b = Dec(b) # Ditto
print(a-b)
который выводит -1
. Если вы повторите это в интерактивном интерпретаторе вместо использования print
, вы увидите Decimal('-1')
, который является repr
формой Decimal
s, но это численно -1
, и если преобразовать в int
или преобразовать в строку с помощью любого метода, который не использует repr
, например. print
, отображается просто -1
.
Ошибка с плавающей запятой.