Python Numbers

Number Types

  • int: integral number
  • float: floating point number, usually IEEE 754 (64 bits, base 2)
  • complex: complex number, implemented as two floats
  • decimal.Decimal: floating point number stored in base 10, arbitrary precision
  • fractions.Fraction: rational, numerator/denominator; automatically compute the greatest common divisor (GCD) to simplify the fraction

Number Tower

The numbers module is specified by the PEP 3141 – A Type Hierarchy for Numbers.

  • numbers.Number: base class
  • numbers.Complex: Add real, imag, conjugate()
  • numbers.Real: subclass of Complex; add many float operations.
  • numbers.Rational: subclass of Real; add numerator and denominator attributes
  • numbers.Integral: subclass of Rational

Subclasses:

  • numbers.Number: int, float, complex, decimal.Decimal, fractions.Fraction
  • numbers.Complex: int, float, complex, fractions.Fraction
  • numbers.Real: int, float, fractions.Fraction
  • numbers.Rational: int, fractions.Fraction
  • numbers.Integral: int

int, float, complex methods and attributes:

  • conjugate()
  • imag
  • real

int and Fraction attributes:

  • denominator
  • numerator

Conversions in Python

int(obj) and float(obj) accept:

  • int
  • float
  • complex
  • decimal.Decimal
  • fractions.Fraction
  • bytes
  • str

int(obj) rounds towards zero (ROUND_DOWN, ex: int(0.9) == 0 and int(-0.9) == 0).

But int(obj) and float(obj) reject:

  • complex

complex(obj) accepts:

  • int
  • float
  • complex
  • decimal.Decimal
  • fractions.Fraction
  • bytes
  • str

But complex(obj) rejects:

  • bytes

decimal.Decimal(obj) accepts:

  • int
  • float
  • complex
  • decimal.Decimal
  • fractions.Fraction
  • bytes
  • str

But decimal.Decimal(obj) rejects:

  • complex
  • fractions.Fraction
  • bytes

fractions.Fraction(obj) accepts:

  • int
  • float
  • complex
  • decimal.Decimal
  • fractions.Fraction
  • bytes
  • str (ex: "1" and "1/2")

But fractions.Fraction(obj) rejects:

  • complex
  • bytes

int type

Examples:

>>> (123).bit_length()
7
>>> sys.int_info
sys.int_info(bits_per_digit=30,
             sizeof_digit=4)

Serialization as bytes:

>>> (123).to_bytes(4, 'little')
b'{\x00\x00\x00'
>>> int.from_bytes(b'{\x00\x00\x00', 'little')
123

Rounding:

  • int(float) calls float.__trunc__(), so rounds towards zero (ROUND_DOWN)

float type

Examples:

>>> sys.float_info
sys.float_info(max=1.7976931348623157e+308,
               max_exp=1024,
               max_10_exp=308,
               min=2.2250738585072014e-308,
               min_exp=-1021,
               min_10_exp=-307,
               dig=15,
               mant_dig=53,
               epsilon=2.220446049250313e-16,
               radix=2,
               rounds=1)
>>> sys.float_repr_style
'short'
>>> (1.2).as_integer_ratio()
(5404319552844595, 4503599627370496)

Formatting as hexadecimal (base 16):

>>> (1.1).hex()
'0x1.199999999999ap+0'
>>> float.fromhex('0x1.199999999999ap+0')
1.1

Rounding:

  • float.__trunc__(): Round towards zero (ROUND_DOWN)
  • float.__round__(): Round to nearest with ties going to nearest even integer (ROUND_HALF_EVEN).
  • float.__int__() is an alias to float.__trunc__() (ROUND_DOWN)

Fraction

>>> fractions.Fraction(5, 10)   # int / int
Fraction(1, 2)
>>> fractions.Fraction(1.2)   # float
Fraction(5404319552844595, 4503599627370496)

C API

Convert a Python object to an integer. Type methods:

  • __int__(): slot type->tp_as_number->nb_int
  • __index__(): slot type->tp_as_number->nb_index
  • __trunc__() (no slot)

PyNumber_Long(obj):

  • Call obj.__trunc__() (Round towards zero, ROUND_DOWN)
  • or: If obj type is bytes or str, parse the string in decimal (base 10)
  • or: error!

PyNumber_Index(x) calls the __index__() method: raises an exception if the type has no __index__() method or if method does not return exactly an int type (*).

_PyLong_FromNbInt(x):

  • Call type(x).__int__(x): the result type must be exactly the int type (*)
  • or: error!

_PyLong_FromNbIndexOrNbInt(x): __index__() or __int__():

  • return x if type(x) == int (PyLong_CheckExact())
  • call type(x).__index__(x) is defined: the result type must be exactly the int type (*)
  • call type(x).__int__(x) (call _PyLong_FromNbInt()): the result type must be exactly the int type (*)
  • error if it is not a int subclass, and the type don’t define __index__() nor __int__() method
  • New in Python 3.8

PyLong_AsLong() converts a Python int to a C long:

  • call _PyLong_FromNbIndexOrNbInt()
  • raise OverflowError if the result does not fit into a C long
  • Python 3.7 and older only calls __int__(), not __index__().

PyLong_AsUnsignedLongMask() converts a Python object to a C unsigned long:

  • call _PyLong_FromNbIndexOrNbInt(x) and then _PyLong_AsUnsignedLongMask() on the result
  • error if the object cannot be converted to an int by _PyLong_FromNbIndexOrNbInt(x)
  • mask integer overflow
  • Python 3.7 and older only calls __int__(), not __index__().

(*) Special case: __int__() or __index__() return a int subclass. This feature is deprecated since Python 3.3 (see commit 6a44f6ee). This feature may be removed from Python 3.9: see bpo-17576.

PyArg_ParseTuple and Py_BuildValue

Reference documentation: Parsing arguments and building values.

  • PyArg_ParseTuple is implemented in Python/getargs.c, core function: convertsimple().

  • Py_BuildValue is implemented in Python/modsupport.c, core function: do_mkvalue().

  • Functions behave differently if PY_SSIZE_T_CLEAN is defined:

    For all # variants of formats (s#, y#, etc.), the type of the length argument (int or Py_ssize_t) is controlled by defining the macro PY_SSIZE_T_CLEAN before including Python.h.

PyArg_ParseTuple formats:

  • "i" (C int): __index__() or __int__(); call PyLong_AsLong(obj), but explicitly rejects float using PyFloat_Check(arg).
  • "l" (C long): __index__() or __int__(); call PyLong_AsLong(), but explicitly rejects float using PyFloat_Check(arg)
  • "n" (C ssize_t): __index__(); call PyNumber_Index() and then PyLong_AsSsize_t(). Exception on overflow.
  • "k": call PyLong_AsUnsignedLongMask() if it’s an int or int subclass, error otherwise. Mask integer overflow.

Note: In Python 3.7 and older, PyLong_AsLong() only calls __int__(), not __index__().

Script to update this page

number_tower.py:

from __future__ import print_function
import numbers
import warnings
import fractions
import decimal

warnings.simplefilter("error", DeprecationWarning)

VALUES = (
    ("int", 123),
    ("float", 1.5),
    ("complex", complex(0, 1.5)),
    ("decimal.Decimal", decimal.Decimal("1.1")),
    ("fractions.Fraction", fractions.Fraction(1, 7)),
    ("bytes", b"123"),
    ("str", "123"),
)

TYPES = (
    ("int", int),
    ("float", float),
    ("complex", complex),
    ("decimal.Decimal", decimal.Decimal),
    ("fractions.Fraction", fractions.Fraction),
)

for type_descr, num_type in TYPES:
    accepted = []
    rejected = []
    for value_descr, value in VALUES:
        try:
            num_type(value)
        except TypeError:
            rejected.append(value_descr)
        accepted.append(value_descr)
    if accepted:
        print("``%s`` accept:" % type_descr)
        print()
        for name in accepted:
            print("* ``%s``" % name)
        print()
    if rejected:
        print("``%s`` reject:" % type_descr)
        print()
        for name in rejected:
            print("* ``%s``" % name)
        print()
print()

for tower_name, tower_type in (
    ("Number", numbers.Number),
    ("Complex", numbers.Complex),
    ("Real", numbers.Real),
    ("Rational", numbers.Rational),
    ("Integral", numbers.Integral),
):
    subclasses = []
    for type_descr, num_type in TYPES:
        if issubclass(num_type, tower_type):
            subclasses.append(type_descr)
    print("%s subclasses: %s" % (tower_name, ", ".join(subclasses)))