[Python-ideas] Python Numbers as Human Concept Decimal System

Tim Peters tim.peters at gmail.com
Sun Mar 9 16:52:30 CET 2014


[Ethan Furman]
>> Python 3.4.0b3+ (default:aab7258a31d3, Feb  7 2014, 10:48:46)
>> [GCC 4.7.3] on linux
>> Type "help", "copyright", "credits" or "license" for more information.
>> --> from decimal import Decimal as D
>> --> 9017.0109812864350067128347806
>> 9017.010981286436
>> --> D(9017.0109812864350067128347806)
>> Decimal('9017.01098128643570817075669765472412109375')
>>
>> In case my question isn't obvious, the direct float got me 16 digits, while
>> the Decimal float got me 42 digits.  Why is the Decimal more "accurate"
>> that the float it came from?

[Steven D'Aprano]
> That's an interesting question. It does appear to be a lot more digits
> than needed. Here's that float exactly, in hex:
>
> py> 9017.010981286436.hex()
> '0x1.19c8167d5b50ep+13'
>
> Let's convert it to decimal:
>
>
> py> digits = list('19c8167d5b50e')
> py> for i, c in enumerate(digits):
> ...     digits[i] = '0123456789abcdef'.index(c)
> ...
> py> d = Decimal(0)
> py> for n in digits[::-1]:
> ...     d += n
> ...     d /= 16
> ...
> py> d += 1
> py> d *= 2**13
> py> d
> Decimal('9017.010981286435708170756694')
>
> ,,,
> So I must admit, I'm not sure where the extra digits come from:
>
> Decimal('9017.01098128643570817075669765472412109375')
> Decimal('9017.010981286435708170756694')
>
> but perhaps my calculation was rather naive.

The calculation is fine, but recall that decimal calculations are
limited to 28 significant digits by default.  It's no coincidence that
len('9017.010981286435708170756694') == 29 (28 digits + a decimal
point).  The answer has "suffered" multiple rounding errors to make it
fit in 28 digits.

Boost the context precision before you start, and you'll get more
digits.  Keep boosting it, and you'll eventually get
9017.01098128643570817075669765472412109375 followed by (zero or more)
trailing zeroes.

A more direct way is to view this as an integer problem:  mathematically,

0x1.19c8167d5b50ep+13 is
0x119c8167d5b50e / 2**(4*13 - 13) =
0x119c8167d5b50e / 2**39 =  (multiplying top and bottom by 5**39)
0x119c8167d5b50e * 5**39 / 10**39

and now it's in the form of an integer numerator divided by a power of
10.  The numerator is:

>>>  0x119c8167d5b50e * 5**39
9017010981286435708170756697654724121093750


More information about the Python-ideas mailing list