The decimal module

Documentation

  1. decimal module
  2. sys.float_info

The limits of float precission

import sys
import math

print(f"sys.float_info.dig = {sys.float_info.dig}")

x = 1 / 3
print(x)           #Print 16 digits to the right of the decimal point.
print(f"{x:.16f}") #Does the same thing.
print(f"{x:.20f}") #Print 20 digits to the right of the decimal point.

sys.exit(0)
sys.float_info.dig = 15
0.3333333333333333
0.3333333333333333
0.33333333333333331483
"Smallest positive whole number that cannot be stored in a float."

import sys

f = 2.0 ** 53   #9,007,199,254,740,992.0
print(f)
print(f + 1)
print()

#Format with commas to the left of the decimal point,
#and with one digit to the right of the decimal point.

print(f"{f    :,.1f}")
print(f"{f + 1:,.1f}")
print()

#A plain old int can hold this number with no trouble.

i = 2 ** 53   #9,007,199,254,740,992
print(i)
print(i + 1)
print()

print(f"{i    :,}")
print(f"{i + 1:,}")

sys.exit(0)
9007199254740992.0
9007199254740992.0

9,007,199,254,740,992.0
9,007,199,254,740,992.0

9007199254740992
9007199254740993

9,007,199,254,740,992
9,007,199,254,740,993

What does x really hold?

import sys
import math

mant_dig = sys.float_info.mant_dig
print(f"sys.float_info.mant_dig = {mant_dig}")

x = 1 / 3
print(f"{x:.59f}") #Print 59 digits to the right of the decimal point.
mantissa, exponent = math.frexp(x)
print(f"mantissa = {mantissa}")
print(f"exponent = {exponent}")

print("The mantissa of 1/3 is stored internally as the fraction")
print(f"{int(mantissa * 2**mant_dig):,} / {2**mant_dig:,}")
sys.float_info.mant_dig = 53
0.33333333333333331482961625624739099293947219848632812500000
mantissa = 0.6666666666666666
exponent = -1
The mantissa of 1/3 is stored internally as the fraction
6,004,799,503,160,661 / 9,007,199,254,740,992

Significant digits

x = 10 / 3
print(f"{x:.6f}")   #Print 6 digits to the right of the decimal point.
print(f"{x:.6g}")   #Print 6 significant digits.
3.333333
3.33333

An object of class decimal.Decimal

An object of class decimal.Decimal can hold more digits than an object of class float. The object returned by the function decimal.localcontext is a context manager.

import sys
import decimal

print(f"decimal.getcontext().prec = {decimal.getcontext().prec}")
dividend = decimal.Decimal("1")
divisor  = decimal.Decimal("3")
print(dividend / divisor)     #Print 28 significant digits.
print()

with decimal.localcontext() as context:
    context.prec = 59
    print(f"decimal.getcontext().prec = {decimal.getcontext().prec}")
    print(dividend / divisor) #Print 59 significant digits.

print()
print(f"decimal.getcontext().prec = {decimal.getcontext().prec}")
print(dividend / divisor)     #Back to 28 significant digits.
sys.exit(0)
decimal.getcontext().prec = 28
0.3333333333333333333333333333

decimal.getcontext().prec = 59
0.33333333333333333333333333333333333333333333333333333333333

decimal.getcontext().prec = 28
0.3333333333333333333333333333