views:

134

answers:

6

Is there any difference in precision between one time assignment:

res=n/k

and multiple assignment in for cycle:

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

?

+3  A: 

Sure, they are different, because of how floating point division works.

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

There's a good explanation in the python official tutorial.

nosklo
+1  A: 

Well if k divides n then definitely the first one is more precise :-) To be serious, if the division is floating point and n > 1 then the first one will be more precise anyway though they will probably give different results, as nosklo said.

BTW, in Python 2.6 the division is integer by default so you'll have very different results. 1/k will always give 0 unless k <= 1.

Eli Bendersky
Unless `k` is a float.
Mike Graham
A: 

Floating point arithmetic has representation and roundoff errors. For the types of data floating point numbers are intended to represent, real numbers of reasonable size, these errors are generally acceptable.

If you want to calculate the quotient of two numbers, the right way is simply to say result = n / k (beware if these are both integers and you have not said from __future__ import division, this is not what you may expect). The second way is silly, error-prone, and ugly.

There is some discussion of floating point inexactness in the Python tutorial: http://docs.python.org/tutorial/floatingpoint.html

Mike Graham
A: 

Even if we charitably assume a floating-point division, there's very definitely a difference in precision; the for loop is executed n - 1 times!

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

Also depends on what res is initialised to in the second case :-)

John Machin
+7  A: 

Floating-point division a/b is not mathematical division a ÷ b, except in very rare* circumstances.

Generally, floating point division a/b is a ÷ b + ε.

This is true for two reasons.

  1. Float numbers (except in rare cases) are an approximation of the decimal number.

    a is a + εa.

    b is b + εb.

    Float numbers uses a base 2 encoding of the digits to the right of the decimal place. When you write 3.1, this is expanded to a base-2 approximation that differs from the real value by a small amount.

    Real decimal numbers have the same problem, by the way. Write down the decimal expansion of 1/3. Oops. You have to stop writing decimal places at some point. Binary floating point numbers have the same problem.

  2. Division has a fixed number of binary places, meaning the answer is truncated. If there's a repeating binary pattern, it gets chopped. In rare cases, this doesn't matter. In general, you've introduced error by doing division.

Therefore, when you do something like repeatedly add 1/k values you're computing

1 ÷ k + ε

And adding those up. Your result (if you had the right range) would be

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

You've multiplied the small error, ε, by n. Making it a big error. (Except in rare cases.)

This is bad. Very bad. All floating point division introduces an error. Your job as a programmer is to do the algebra to avoid or defer division to prevent this. Good software design means good algebra to prevent errors being introduced by the division operator.

[* The rare cases. In rare cases, the small error happens to be zero. The rare cases occur when your floating point values are small whole numbers or fractions that are sums of powers of two 1/2, 1/4, 1/8, etc. In the rare case that you have a benign number with a benign fractional part, the error will be zero.]

S.Lott
+1: In my opinion the most comprehensive answer.
gorsky
A: 

Certainly there is a difference if you use floating point numbers, unless the Python interpreter/compiler you are using is capable of optimizing away the loop (Maybe Jython or IronPython might be able to? C compilers are pretty good at this).

If you actually want these two approaches to be the same precision though, and you are using integers for your numerator and denominator, you can use the python fractions package

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)
kibibu