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

Что более точно, x **. 5 или math.sqrt(x)?

Недавно я обнаружил, что x**.5 и math.sqrt(x) не всегда дают одинаковый результат в Python:

Python 2.6.1 (r261:67517, Dec 4 2008, 16:51:00) [MSC v.1500 32 bit (Intel)]
on win32
>>> 8885558**.5 - math.sqrt(8885558)
-4.5474735088646412e-13

Проверяя все целые числа ниже 10 ** 7, два метода дали разные результаты почти для 0,1% образцов, причем размер ошибки увеличивается (медленно) для больших чисел.

Итак, вопрос в том, какой метод более точен?

4b9b3361

Ответ 1

Ни один из них не является более точным, они оба отклоняются от фактического ответа в равных частях:

>>> (8885558**0.5)**2
8885557.9999999981
>>> sqrt(8885558)**2
8885558.0000000019

>>> 2**1023.99999999999
1.7976931348498497e+308

>>> (sqrt(2**1023.99999999999))**2
1.7976931348498495e+308
>>> ((2**1023.99999999999)**0.5)**2
1.7976931348498499e+308

>>> ((2**1023.99999999999)**0.5)**2 - 2**1023.99999999999
1.9958403095347198e+292
>>> (sqrt(2**1023.99999999999))**2 - 2**1023.99999999999
-1.9958403095347198e+292

http://mail.python.org/pipermail/python-list/2003-November/238546.html

Математический модуль обертывает платформу C библиотечные математические функции того же имена; math.pow() является наиболее полезным, если вам нужно (или просто хотеть) высоко совместимость с расширениями C вызов C pow().

__builtin__.pow() - это реализация инфикса Python **оператора, и занимается сложными числа, неограниченные целые степени и модульное возведение в степень (C pow() не обрабатывает ни одно из них).

** более полная. math.sqrt - это, вероятно, всего лишь реализация C sqrt, которая, вероятно, связана с pow.

Ответ 2

И функция pow, и функция math.sqrt() могут вычислять результаты, которые являются более точными, чем то, что может хранить поплавочный тип по умолчанию. Я думаю, что ошибки, которые вы видите, являются результатом ограничений математики с плавающей запятой, а не неточностей функций. Кроме того, поскольку, когда разница в ~ 10 ^ (- 13) является проблемой при взятии квадратного корня из 7-значного числа? Даже самые точные вычисления физики редко требуют, чтобы многие значащие цифры...

Другая причина использования math.sqrt() заключается в том, что ее легче читать и понимать, что обычно является хорошей причиной для того, чтобы делать что-то определенным образом.

Ответ 3

Используйте decimal для поиска более точных квадратных корней:

>>> import decimal
>>> decimal.getcontext().prec = 60
>>> decimal.Decimal(8885558).sqrt()
Decimal("2980.86531061032678789963529280900544861029083861907705317042")

Ответ 4

Каждый раз, когда вам предоставляется выбор между двумя функциями, которые встроены в язык, более конкретная функция почти всегда будет равна или лучше, чем общая (поскольку, если бы это было хуже, кодеры только что были реализованы это с точки зрения общей функции). Sqrt более специфичен, чем общее возведение в степень, поэтому вы можете ожидать, что это лучший выбор. И это, по крайней мере, с точки зрения скорости. С точки зрения точности вы не имеете достаточной точности в своих номерах, чтобы иметь возможность сказать.

Примечание. Чтобы уточнить, sqrt работает быстрее в Python 3.0. Это медленнее в более старых версиях Python. См. Измерения J.F. Себастьяна в Что происходит быстрее в Python: x **. 5 или math.sqrt(x)?.

Ответ 5

Это должно быть какая-то конкретная платформа, потому что я получаю разные результаты:

Python 2.5.1 (r251:54863, Jan 13 2009, 10:26:13) 
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
>>> 8885558**.5 - math.sqrt(8885558)
0.0

Какую версию python вы используете и какую ОС?

Я предполагаю, что это как-то связано с продвижением и литьем. Другими словами, поскольку вы делаете 8885558 **. 5, 8885558 должен быть повышен до плавания. Все это обрабатывается по-разному в зависимости от операционной системы, процессора и версии Python. Добро пожаловать в удивительный мир арифметики с плавающей запятой.: -)

Ответ 6

У меня возникла такая же проблема с Win XP Python 2.5.1, в то время как я не на 32-битном Gentoo Python 2.5.4. Это вопрос реализации библиотеки C.

Теперь, в Win, math.sqrt(8885558)**2 дает 8885558.0000000019, а (8885558**.5)**2 дает 8885557.9999999981, который, похоже, равен одному и тому же эпсилону.

Я говорю, что нельзя сказать, какой из них является лучшим вариантом.

Ответ 7

Я не получаю такого же поведения. Возможно, ошибка связана с платформой? На amd64 я получаю следующее:

Python 2.5.2 (r252:60911, Mar 10 2008, 15:14:55) 
[GCC 3.3.5 (propolice)] on openbsd4
Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>> math.sqrt(8885558) - (8885558**.5)
0.0
>>> (8885558**.5) - math.sqrt(8885558)
0.0

Ответ 8

В теории math.sqrt должна иметь более высокую точность, чем математика. См. Метод Ньютона для вычисления квадратных корней [0]. Однако ограничение числа десятичных цифр плавающего элемента python (или двойника C), вероятно, замаскирует разницу.

[0] http://en.wikipedia.org/wiki/Integer_square_root

Ответ 9

Я выполнил тот же тест и получил те же результаты, 10103 отличия от 10000000. Это было использование Python 2.7 в Windows.

Разница заключается в округлении. Я считаю, что, когда эти два результата отличаются друг от друга, это только один ULP, который является наименьшей возможной разницей для поплавка. Истинный ответ лежит между ними, но float не имеет возможности точно представлять его, и он должен быть округлен.

Как отмечено в другом ответе, decimal module можно использовать для повышения точности, чем float. Я использовал это, чтобы лучше понять истинную ошибку, и во всех случаях sqrt был ближе, чем **0.5. Хотя не много!

>>> s1 = sqrt(8885558)
>>> s2 = 8885558**0.5
>>> s3 = decimal.Decimal(8885558).sqrt()
>>> s1, s2, s3
(2980.865310610327, 2980.8653106103266, Decimal('2980.865310610326787899635293'))
>>> s3 - decimal.Decimal(s1)
Decimal('-2.268290468226740188598632812E-13')
>>> s3 - decimal.Decimal(s2)
Decimal('2.2791830406379010009765625E-13')