Почему разница между различными плавающими числами может быть равна 0 в python?

Почему результат кода ниже 0 в python3?

a = "4.15129406851375e+17"
a = float(a)
b = "415129406851375001"
b = float(b)
a-b

Ошибка с плавающей запятой.

Scott Hunter 26.01.2023 15:10

Прочитав документацию чего конкретно вы ожидали в качестве другого результата?

Damien_The_Unbeliever 26.01.2023 15:11

@khelwood: это не лучший дубликат; он охватывает «Математика с плавающей запятой на самом деле нарушена (для определенного определения сломанной)», но проблемы здесь связаны с превышением ограничений float для представления целых чисел, а не с точностью справа от десятичной дроби (даже если две проблемы в некоторой степени связаны).

ShadowRanger 26.01.2023 15:13

используйте встроенный пакет decimal для преобразования ваших строк в decimal.Decimal, тогда он напечатает правильное значение -1

Gábor Fekete 26.01.2023 15:14

Проголосовали за повторное открытие: похоже на еще один случай, когда закрытое голосование связано с общей проблемой (о которой ОП может даже знать), но здесь более уместен более точный ответ.

9769953 26.01.2023 15:30

Спасибо @кая! Математика с плавающей точкой не работает? всегда полезная ссылка, а не дубликат (ответы косвенно касаются связанной информации, без подробностей), но я не смог найти дубликаты, характерные для потери точности float для больших чисел.

ShadowRanger 26.01.2023 16:25
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
6
69
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Это происходит потому, что и 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 формой Decimals, но это численно -1, и если преобразовать в int или преобразовать в строку с помощью любого метода, который не использует repr, например. print, отображается просто -1.

Попробуйте онлайн!

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