[Python-ideas] Python Float Update
Chris Angelico
rosuav at gmail.com
Mon Jun 1 04:48:12 CEST 2015
On Mon, Jun 1, 2015 at 12:25 PM, u8y7541 The Awesome Person
<surya.subbarao1 at gmail.com> wrote:
>
> I will be presenting a modification to the float class, which will improve its speed and accuracy (reduce floating point errors). This is applicable because Python uses a numerator and denominator rather than a sign and mantissa to represent floats.
>
> First, I propose that a float's integer ratio should be accurate. For example, (1 / 3).as_integer_ratio() should return (1, 3). Instead, it returns(6004799503160661, 18014398509481984).
>
I think you're misunderstanding the as_integer_ratio method. That
isn't how Python works internally; that's a service provided for
parsing out float internals into something more readable. What you
_actually_ are working with is IEEE 754 binary64. (Caveat: I have no
idea what Python-the-language stipulates, nor what other Python
implementations use, but that's what CPython uses, and you did your
initial experiments with CPython. None of this discussion applies *at
all* if a Python implementation doesn't use IEEE 754.) So internally,
1/3 is stored as:
0 <-- sign bit (positive)
01111111101 <-- exponent (1021)
0101010101010101010101010101010101010101010101010101 <-- mantissa (52
bits, repeating)
The exponent is offset by 1023, so this means 1.010101.... divided by
2²; the original repeating value is exactly equal to 4/3, so this is
correct, but as soon as it's squeezed into a finite-sized mantissa, it
gets rounded - in this case, rounded down.
That's where your result comes from. It's been rounded such that it
fits inside IEEE 754, and then converted back to a fraction
afterwards. You're never going to get an exact result for anything
with a denominator that isn't a power of two. Fortunately, Python does
offer a solution: store your number as a pair of integers, rather than
as a packed floating point value, and all calculations truly will be
exact (at the cost of performance):
>>> one_third = fractions.Fraction(1, 3)
>>> one_eighth = fractions.Fraction(1, 8)
>>> one_third + one_eighth
Fraction(11, 24)
This is possibly more what you want to work with.
ChrisA
More information about the Python-ideas
mailing list