Python: почему 0.01.as_integer_ratio () возвращает 5764607523034235/576460752303423488 - PullRequest
3 голосов
/ 09 апреля 2020

Я работал над вопросом 33 Проекта Эйлера в Python, когда я заметил что-то, чего я не понимаю.

Для ответа на вопрос требовался знаменатель, указанный в его наименьшем общем числе сроки. Поэтому я решил использовать float.as_integer_ratio() для проверки знаменателя. Оказывается, что 0.01.as_integer_ratio() возвращает (5764607523034235, 576460752303423488), а 0.1.as_integer_ratio() возвращает (3602879701896397, 36028797018963968) вместо ожидаемых 1/100 и 1/10.

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

Ответы [ 2 ]

1 голос
/ 10 апреля 2020

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

>>> from fractions import Fraction
>>> Fraction(0.01).limit_denominator(100000)
1/100
0 голосов
/ 09 апреля 2020

Компьютер обычно не может рассчитывать с десятичными числами на основание 10. Я не уверен, отличается ли он для квантового компьютера, но обычно числа рассчитываются внутренне для основания 2.

Без этого знания это может быть шокирующим моментом:

>>> 0.1 + 0.1 + 0.1 == 0.3
False

Это означает, что обычно используется приближение, чтобы получить хорошее представление об этих числах. Из-за этих приближений 0.1 + 0.1 + 0.1 не является 0.3.

Так что простой 0.1 (основание 10) становится 0.0001 1001 1001 1001... (основание 2, бесконечное). Но фракции для спасения и избавления от приближений! float.as_integer_ratio() хочет быть точным:

Вернуть пару целых чисел, чье отношение точно равно к исходному значению с плавающей точкой и знаменателю.

(см. Python документация , выделено мое.)

Таким образом, метод использует алгоритм для вычисления точного отношения, например, из 0.1 и наилучших чисел, которые алгоритм может найдено 3602879701896397 и 36028797018963968. Кажется, что числа хорошие, потому что библиотека фракций получает те же результаты (как вы сказали).

Кстати: все хорошо в базе 10, если вы рассчитываете десятичные числа, которые вы можете обрабатывать также в база 2, например 0.5 (= 0.1 на базу 2):

>>> 0.5.as_integer_ratio()
(1, 2)

Если вы хотите прочитать больше, есть также хороший сайт с множеством подробной информации и дальнейшими ссылками в Python учебники .

...