Why isn't Python king of the hill?
Tim Peters
tim.one at home.com
Sun Jun 3 21:23:00 EDT 2001
[Nick Perkins]
> >>> from math import *
> >>> sqrt(2)**2
> 2.0000000000000004
> >>> sin(pi)
> 1.2246063538223773e-016
>
> Neither of these 'errors' would be eliminated by using Rationals.
> I am sure there are many similar examples.
>
> note that:
> >>> cos(pi)
> -1.0
> actually works, for some reason (luck?)
Floats aren't random -- so luck isn't *really* an issue. What is an issue
is that the mathematical PI isn't exactly representable as a float (and
binary vs decimal has nothing to do with *this* one), so the machine pi is
really some other number,
pi = PI + tiny
where "tiny" is an error in the last bit (or so) introduced by finite
precision. Using angle sum formulas,
sin(pi) =
sin(PI+tiny) = cos(tiny)*sin(PI) + sin(tiny)*cos(PI) =
cos(tiny)*0 + sin(tiny)*(-1) =
-sin(tiny) =
[expanding]
-tiny - tiny**3/6 - ...
tiny**3/6 is too small to have any effect when added to tiny (finite
precision again), so the result you see is plain old -tiny. If you got 0
instead, it would be wrong! If you carry on the example,
>>> math.sin(math.pi)
1.2246063538223773e-016
>>> math.log(_)/math.log(2)
-52.858531444606918
>>>
you can see the result is about 2**-52, right where "it should be" for a
low-bit error in PI given that floats have 53 bits of precision.
For the other one,
cos(pi) =
cos(PI+tiny) = cos(PI)*cos(tiny) - sin(PI)*sin(tiny) =
(-1)*cos(tiny) - 0*sin(tiny) =
-cos(tiny) =
[expanding]
-1 - tiny**2/2 - ...
Since tiny is about 2**-52, tiny**2/2 is about 2**-105, and that's too small
to have any effect when added to the 53-bit value of -1.0. So -1.0 on the
nose is what you get.
Of course sin() and cos() aren't actually *implemented* like that, but a
careful implementation will deliver the same result in the end.
More information about the Python-list
mailing list