2009-05-31 2 views
8

Я работаю ежедневно с Python 2.4 в моей компании. Я использовал универсальную функцию логарифма «журнал» из стандартной математической библиотеки, и когда я ввел журнал (2 ** 31, 2), он возвратил 31.000000000000004, что показалось мне немного странным.Неточный Логарифм в Python

Я сделал то же самое с другими полномочиями 2, и он отлично работал. Я побежал 'log10 (2 ** 31)/log10 (2)', и я получил раунд 31.0

Я попытался запустить ту же оригинальную функцию в Python 3.0.1, предполагая, что она была исправлена ​​в более продвинутой версии.

Почему это происходит? Возможно ли, что есть некоторые неточности в математических функциях в Python?

+0

дубликата многолетних точек вопроса Плавучего (? почему я получаю сообщение об ошибке с плавающей точкой), не может найти лучший дубликат Q на сообщение, может быть, кто-то другой может. –

+0

Я должен отметить, что Python 3 сделал * not * исправил ошибку с плавающей запятой. Вместо этого на выходе печати используется интеллектуальный алгоритм для отображения предполагаемого значения с плавающей запятой, а не слабины. –

ответ

44

Этого можно ожидать с компьютерной арифметикой. Он следует определенным правилам, таким как IEEE 754, которые, вероятно, не соответствуют математике, которую вы изучили в школе.

Если это фактически вопросов, используйте Python's decimal type.

Пример:

from decimal import Decimal, Context 
ctx = Context(prec=20) 
two = Decimal(2) 
ctx.divide(ctx.power(two, Decimal(31)).ln(ctx), two.ln(ctx)) 
+22

+1 для приятного ответа, но в основном для «Если это действительно имеет значение». Зонды были доставлены в Сатурн с меньшей точностью. – dwc

+0

Действительно. Курсив - самая важная часть ответа. –

+0

@ dwc Было бы важно, если бы ФП взял бы на себя выполнение результата функции журнала. Тогда ошибка станет очень большой. В моем случае в одной из моих программ я делал это: 'a = floor (log (x, b))', и программа сбивала несколько строк вперед, потому что 'floor (log (243,3))' был выйдет 4 – Rushil

17

Всегда предположить, что операции с плавающей точкой будет иметь некоторые ошибки в них и проверить равенство принимает эту ошибку во внимание (либо процентное значение, как 0,00001% или фиксированное значение, как 0.00000000001). Эта неточность является заданной, поскольку не все десятичные числа могут быть представлены в двоичном формате с фиксированным числом бит.

Ваш частный случай не является одним из них, если Python использует IEEE754, так как 31 должен быть легко представлен с одной точностью. Однако возможно, что он теряет точность в одном из многих шагов, необходимых для вычисления журнала , просто потому, что он не имеет кода для обнаружения особых случаев, таких как прямая мощность двух.

+1

+1 Я думаю, что у вас есть ручка на нем с последним предложением. –

+0

Очень интересно. Я пишу код очень долго, и это первый случай, когда я сталкивался с этим явлением. Но после ответа здесь, я думаю, теперь я начинаю видеть большую картину того, почему это происходит. –

5

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

В целом, совершенно неправильно предполагать, что операции с плавающей запятой точны, особенно с одинарной точностью. "Accuracy problems" section из Википедии Статья с плавающей точкой :)

2

Это нормально. Я ожидал бы, что log10 будет более точным, чем log (x, y), так как он точно знает, что такое база логарифма, также может быть некоторая аппаратная поддержка для вычисления логарифмов базы 10.

19

Вы должны прочитать «Что каждый компьютерный ученый должен знать о арифметике с плавающей точкой».

http://docs.sun.com/source/806-3568/ncg_goldberg.html

+1

Большое спасибо. Это могучая ссылка. –

+0

+1 для отличной ссылки –

3

IEEE числа с плавающей точкой двойной имеют 52 bits of precision. Начиная с 10^15 < 2^52 < 10^16, двойной имеет от 15 до 16 значащих цифр. Результат 31.000000000000004 соответствует 16 цифрам, поэтому он так же хорош, как вы можете ожидать.

1

repr esentation (float.__repr__) ряд в Python пытается вернуть строку цифр как можно ближе к реальной стоимости, как это возможно, когда преобразуется обратно, учитывая, что IEEE-754 арифметического точен до предела.В любом случае, если вы print эд результат, вы не заметили бы:

>>> from math import log 
>>> log(2**31,2) 
31.000000000000004 
>>> print log(2**31,2) 
31.0 

print преобразует свои аргументы в строки (в данном случае, с помощью метода float.__str__), который обслуживает неточность, показывая меньше цифр :

>>> log(1000000,2) 
19.931568569324174 
>>> print log(1000000,2) 
19.9315685693 
>>> 1.0/10 
0.10000000000000001 
>>> print 1.0/10 
0.1 

usuallyuseless' ответ очень полезно, на самом деле :)