Python: какой способ дает лучшую точность - PullRequest
0 голосов
/ 03 марта 2010

Есть ли разница в точности между однократным назначением:

res=n/k

и многократное назначение в течение цикла:

for i in range(n):
    res+=1/k

Ответы [ 6 ]

8 голосов
/ 03 марта 2010

Деление с плавающей точкой a/b не является математическим делением a ÷ b , за исключением очень редких * обстоятельств.

Как правило, деление с плавающей запятой a/b равно a ÷ b + ε.

Это верно по двум причинам.

  1. Числа с плавающей точкой (за исключением редких случаев) являются приблизительными значениями десятичного числа.

    a - это a + εa.

    b - это b + εb.

    Числа с плавающей запятой используют основную кодировку цифр 2 справа от десятичного разряда. Когда вы пишете 3.1, это расширяется до приближения по основанию 2, которое отличается от реального значения на небольшое количество.

    Кстати, реальные десятичные числа имеют ту же проблему. Запишите десятичное расширение 1/3. К сожалению. Вы должны прекратить писать десятичные разряды в какой-то момент. Двоичные числа с плавающей запятой имеют ту же проблему.

  2. Деление имеет фиксированное количество двоичных разрядов, что означает усеченный ответ. Если есть повторяющийся двоичный шаблон, он прерывается. В редких случаях это не имеет значения. В общем, вы ввели ошибку, выполнив деление.

Поэтому, когда вы делаете что-то подобное, многократно добавляйте 1/k значения, которые вы вычисляете

1 ÷ k + ε

И добавляя их. Ваш результат (если вы имели право range) будет

n × (1 ÷ k + ε) = n ÷ k + n × ε

Вы умножили небольшую ошибку ε на n . Делать это большой ошибкой. (За исключением редких случаев.)

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

[* Редкие случаи. В редких случаях маленькая ошибка оказывается равной нулю. Редкие случаи происходят, когда ваши значения с плавающей запятой представляют собой небольшие целые числа или дроби, которые представляют собой суммы степеней двух 1/2, 1/4, 1/8 и т. Д. В редком случае, когда у вас есть доброкачественное число с доброкачественным дробным числом часть, ошибка будет равна нулю.]

3 голосов
/ 03 марта 2010

Конечно, они разные из-за того, как работает деление с плавающей запятой.

>>> res = 0
>>> for x in xrange(5000): res += 0.1
... 
>>> res == 5000 * 0.1
False

В официальном руководстве по питону есть хорошее объяснение .

1 голос
/ 03 марта 2010

Хорошо, если k делит n, то, безусловно, первый более точный :-) Если быть серьезным, если деление с плавающей запятой и n > 1, то первое в любом случае будет более точным, хотя они, вероятно, дают разные результаты, как сказал носкло.

Кстати, в Python 2.6 деление по умолчанию целочисленное, поэтому вы получите очень разные результаты. 1/k всегда будет давать 0, если k <= 1.

0 голосов
/ 04 марта 2010

Конечно, есть разница, если вы используете числа с плавающей запятой, если только интерпретатор / компилятор Python, который вы используете, не способен оптимизировать цикл (возможно, Jython или IronPython могут это сделать? Компиляторы C довольно хороши в этом).

Если вы действительно хотите, чтобы эти два подхода имели одинаковую точность, то и вы используете целые числа для числителя и знаменателя , вы можете использовать Python дроби пакет

from fractions import Fraction
n,k = 999,1000
res = Fraction(0,1)

for i in range(0,n):
    res += Fraction(1,k)

print float(res)
0 голосов
/ 03 марта 2010

Даже если мы мягко предполагаем деление с плавающей запятой, разница в точности очень определенная; цикл for выполняется n - 1 раза!

assert (n-1) / k != n / k

Также зависит от того, к чему инициализируется res во втором случае: -)

0 голосов
/ 03 марта 2010

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

Если вы хотите вычислить частное от двух чисел, правильный способ - просто сказать result = n / k (будьте осторожны, если они оба целые и вы не сказали from __future__ import division, это не то, что вы можете ожидать). Второй способ - глупый, подверженный ошибкам и уродливый.

В уроке по Python обсуждается неточность с плавающей запятой: http://docs.python.org/tutorial/floatingpoint.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...