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

Antoine Pitrou solipsis at pitrou.net
Sun Mar 9 15:59:56 CET 2014


On Sat, 08 Mar 2014 13:59:32 -0800
Ethan Furman <ethan at stoneleaf.us> wrote:
> So, how is this justified?
> 
> 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?

Both representations need to uphold the round-tripping property:

  float(str(some_float)) == some_float
  Decimal(str(some_decimal)) == some_decimal

However, since Decimal has arbitrary precision (as opposed to a fixed
number of bits), the number of digits needed in the repr() can be much
higher in order to uphold the round-tripping property:

  >>> float('1.1') == 1.1
  True
  >>> decimal.Decimal('1.1') == decimal.Decimal(1.1)
  False

Moreover, the Decimal constructor strives to uphold the property that
Decimal(some_number) == some_number (using the required internal
precision).

Therefore, decimal.Decimal(1.1) is the exact decimal value of the
binary floating-point number *best approaching* the decimal literal '1.1':

  >>> decimal.Decimal(1.1)
  Decimal('1.100000000000000088817841970012523233890533447265625')
  >>> decimal.Decimal('1.100000000000000088817841970012523233890533447265625') == 1.1
  True

Indeed, the difference is less than a binary floating-point mantissa's
resolution:

  >>> decimal.Decimal(1.1) - decimal.Decimal('1.1')
  Decimal('8.881784197001252323389053345E-17')
  >>> 2**(-sys.float_info.mant_dig)
  1.1102230246251565e-16

However, since Decimal has such a high potential resolution, changing even one
faraway digit will break the equality:

  >>> decimal.Decimal('1.100000000000000088817841970012523233890533447265626') == 1.1
  False

  (I changed the trailing '5' to a '6')

Which is why Decimal(1.1) has to be so precise, and so has its repr() too.

Of course, if you convert those literals to floats, they turn out to convert to
the same binary floating-point number:

  >>> 1.1 == 1.100000000000000088817841970012523233890533447265625
  True

float() can therefore afford to have a "user-friendly" repr() in some
cases where Decimal can't.

Regards

Antoine.




More information about the Python-ideas mailing list