PyLongObject digit array for arbitrary precision, PyFloatObject IEEE 754 storage, and complex number layout.
Python’s numeric objects are ordinary objects with specialized implementations. They participate in the same object model as lists, dictionaries, functions, classes, and modules: each value has an object header, a type pointer, reference counting behavior, and type slots for operations.
The main built-in numeric types are:
| Type | Python name | Main representation |
|---|---|---|
| Integer | int | Arbitrary-precision integer |
| Boolean | bool | Singleton subclass of int |
| Floating point | float | C double |
| Complex | complex | Pair of C doubles |
These types look simple at the Python level, but each carries important runtime tradeoffs.
17.1 Numeric Objects Are Objects
A Python integer is not normally stored as a raw CPU integer in Python variables.
x = 42At the CPython level, x refers to a Python integer object.
Conceptually:
x ---> PyLongObject
object header
integer payloadThe same is true for floats and complex numbers.
a = 1.5
b = 1 + 2jConceptually:
a ---> PyFloatObject
object header
double value
b ---> PyComplexObject
object header
double real
double imagThe object model gives numbers normal Python behavior:
print(type(42))
print((42).__class__)
print((42).bit_length())Numeric values have methods, participate in protocols, and are passed by reference like all other objects.
17.2 Integer Objects
Python int is arbitrary precision.
x = 10 ** 100
print(x)The value does not overflow at 32 or 64 bits. CPython grows the internal representation as needed.
The C-level type is commonly called PyLongObject.
Conceptually:
PyLongObject
PyVarObject header
ob_size
digit arrayThe ob_size field encodes both the number of internal digits and the sign. The digit array stores the absolute value in a large base.
This is why Python can compute:
x = 2 ** 1000
y = x * x
print(y)without integer overflow.
The cost is that large integers require more memory and more CPU time than machine integers.
17.3 Integer Digit Storage
CPython stores an integer as multiple internal digits.
A simplified model:
value = sign * (d0 + d1 * base + d2 * base^2 + ...)For small integers, only one digit may be needed.
For large integers, many digits are needed.
Conceptually:
42
sign = positive
digits = [42]
2**1000
sign = positive
digits = [d0, d1, d2, ...]This representation is similar to how humans write decimal numbers as digits, except CPython uses a much larger internal base for efficiency.
Arithmetic operations work over these digit arrays.
small int addition
cheap
large int addition
cost grows with number of digits
large int multiplication
cost grows more quickly, with specialized algorithms for large cases17.4 Integer Sign
The sign is represented through the size metadata, not as a separate Python object.
Conceptually:
ob_size > 0
positive integer
ob_size == 0
zero
ob_size < 0
negative integerThe digit array stores magnitude.
Example:
123
ob_size = positive digit count
digits = magnitude
-123
ob_size = negative digit count
digits = magnitudeThis representation keeps the object compact.
17.5 Integer Immutability
Integers are immutable.
x = 10
x += 1This does not mutate the integer object 10. It creates or retrieves another integer object and rebinds x.
Conceptually:
x ---> int object 10
x += 1
x ---> int object 11This behavior is visible when objects are shared:
a = 10
b = a
a += 1
print(a) # 11
print(b) # 10b still refers to the original integer object.
17.6 Small Integer Reuse
CPython reuses some small integer objects.
a = 1
b = 1Both names may refer to the same runtime-owned integer object.
This is an optimization. It reduces allocation for common values.
Do not rely on identity for integer value comparison.
Correct:
if x == 1:
...Incorrect:
if x is 1:
...is tests object identity. == tests numeric equality.
17.7 Boolean Objects
bool is a subclass of int.
print(isinstance(True, int)) # True
print(True + True) # 2There are exactly two boolean singleton objects:
True
FalseThe type relationship is:
bool
subclass of intThis exists for historical and practical compatibility. Boolean values work in numeric contexts, but code should use them for truth values.
if ready:
...Rather than:
if ready == True:
...At the C level, extension code commonly returns booleans with:
Py_RETURN_TRUE;
Py_RETURN_FALSE;These macros return owned references to the singleton objects.
17.8 Truth Value Testing
Numeric objects participate in truth value testing.
bool(0) # False
bool(1) # True
bool(-1) # True
bool(0.0) # False
bool(0j) # FalseThe rule for numeric types is simple:
zero value
false
nonzero value
trueTruth testing uses type protocol machinery. At the C level, numeric types provide behavior through slots such as boolean conversion.
17.9 Integer Operations
Common integer operations include:
a + b
a - b
a * b
a // b
a % b
a ** b
a << n
a >> n
a & b
a | b
a ^ b
~aThese map to numeric slots.
Conceptually:
PyLong_Type
nb_add
nb_subtract
nb_multiply
nb_floor_divide
nb_remainder
nb_power
nb_lshift
nb_rshift
nb_and
nb_or
nb_xor
nb_invertEach operation receives Python objects and returns a Python object.
Even when the result fits in a machine word, the result is a Python object.
17.10 Integer Division
Python has two main division operators.
a / b # true division
a // b # floor divisionFor integers:
print(5 / 2) # 2.5
print(5 // 2) # 2/ returns a float for ordinary integers.
// returns the floor result.
For negative values:
print(-5 // 2) # -3
print(-5 % 2) # 1The identity holds:
a == (a // b) * b + (a % b)with % carrying the sign convention required by Python semantics.
17.11 Bit Operations
Integers support bit operations as though represented in two’s complement with infinite sign extension.
x & y
x | y
x ^ y
~x
x << n
x >> nExample:
print(5 & 3) # 1
print(5 | 3) # 7
print(5 ^ 3) # 6
print(~5) # -6The result of ~x follows:
~x == -x - 1This model is different from fixed-width C integer behavior because Python integers do not have a fixed bit width.
17.12 Integer Methods
Integer methods expose useful properties of the internal value.
x = 1024
print(x.bit_length())
print(x.bit_count())bit_length returns the number of bits needed to represent the absolute value.
print((0).bit_length()) # 0
print((1).bit_length()) # 1
print((2).bit_length()) # 2
print((3).bit_length()) # 2bit_count returns the population count of the absolute value.
print((7).bit_count()) # 3These methods often map efficiently to internal digit operations and CPU intrinsics where available.
17.13 Integer Conversion to Bytes
Integers can be converted to and from byte sequences.
x = 1024
data = x.to_bytes(2, byteorder="big")
print(data)
again = int.from_bytes(data, byteorder="big")
print(again)This matters for:
binary protocols
cryptography
file formats
network byte order
serializationEndianness must be explicit.
x.to_bytes(4, "big")
x.to_bytes(4, "little")The same integer produces different byte layouts depending on byte order.
17.14 Float Objects
Python float is usually a wrapper around a C double.
Conceptually:
PyFloatObject
PyObject header
double valueA float object is fixed-size.
x = 1.5
y = 2.25
print(x + y)Float operations are implemented through numeric slots and platform floating-point operations.
The key point: floats are approximate binary floating-point numbers.
17.15 Binary Floating-Point
Decimal fractions often cannot be represented exactly in binary.
print(0.1 + 0.2)This commonly prints:
0.30000000000000004The issue is representation. 0.1 has no finite binary expansion, just as 1/3 has no finite decimal expansion.
So the stored value is the nearest representable binary float.
This affects equality:
print(0.1 + 0.2 == 0.3) # FalseUse tolerance-based comparison for approximate numeric work:
import math
math.isclose(0.1 + 0.2, 0.3)17.16 Float Special Values
Floats include special values:
inf = float("inf")
nan = float("nan")
neg_inf = float("-inf")Infinity behaves as expected in many comparisons:
print(inf > 1e308) # TrueNaN is unusual:
nan = float("nan")
print(nan == nan) # False
print(nan != nan) # TrueNaN means “not a number.” It does not compare equal to itself.
This behavior follows floating-point rules, not ordinary object identity logic.
17.17 Float Hashing and Equality
Numeric types coordinate equality and hashing.
print(1 == 1.0) # True
print(hash(1) == hash(1.0))If two numeric objects compare equal, they must have the same hash.
This allows dictionaries to behave correctly:
d = {}
d[1] = "int"
d[1.0] = "float"
print(d)The second assignment updates the same key position because 1 == 1.0.
This cross-type numeric equality is convenient, but it can surprise users who expect type-distinct keys.
17.18 Float Conversion
Conversions:
float(1)
int(1.9)
round(1.9)int truncates toward zero:
print(int(1.9)) # 1
print(int(-1.9)) # -1round follows Python’s rounding rules:
print(round(2.5))
print(round(3.5))Floating-point to integer conversion can lose information if the float does not exactly represent the intended value.
For decimal financial arithmetic, use decimal.Decimal rather than binary float.
17.19 Complex Objects
Python complex stores two floating-point values:
PyComplexObject
PyObject header
double real
double imagExample:
z = 3 + 4j
print(z.real)
print(z.imag)The real and imaginary parts are floats.
Complex numbers support arithmetic:
a = 1 + 2j
b = 3 + 4j
print(a + b)
print(a * b)They do not support ordering:
(1 + 2j) < (3 + 4j) # TypeErrorThere is no natural total ordering for complex numbers in Python’s numeric model.
17.20 Complex Arithmetic
Complex multiplication follows:
(a + bi)(c + di) = (ac - bd) + (ad + bc)iPython handles this inside the complex type implementation.
Example:
a = 1 + 2j
b = 3 + 4j
print(a * b) # (-5+10j)Division and powers are also supported.
For advanced numerical work, the cmath module provides complex-aware mathematical functions.
import cmath
print(cmath.sqrt(-1))17.21 Numeric Tower
Python’s numeric model has a conceptual hierarchy:
numbers.Number
numbers.Complex
numbers.Real
numbers.Rational
numbers.IntegralBuilt-in types roughly fit as:
| Type | Conceptual role |
|---|---|
complex | Complex |
float | Real |
int | Integral |
bool | Integral subclass in practice |
The numbers module provides abstract base classes for numeric protocols.
Most ordinary CPython operations use concrete type slots rather than abstract base class dispatch, but the hierarchy helps define expected behavior.
17.22 Mixed Numeric Operations
Mixed numeric operations follow coercion and dispatch rules.
print(1 + 2.5) # 3.5
print(1 + 2j) # (1+2j)
print(1.5 + 2j) # (3.5+2j)Typical widening direction:
int -> float -> complexThis is a conceptual model. The actual implementation uses binary operation dispatch between operand types.
For user-defined numeric classes, returning NotImplemented lets the other operand try reflected behavior.
class N:
def __add__(self, other):
return NotImplemented17.23 __index__
__index__ means an object can be interpreted as an exact integer for indexing and slicing.
class Index:
def __index__(self):
return 2
xs = [10, 20, 30, 40]
print(xs[Index()])This is stricter than __int__.
Use cases include:
list indexes
slice bounds
range arguments
bit operations
low-level integer contextsAt the C level, this corresponds to integer-index protocol behavior.
17.24 __int__, __float__, and __complex__
Conversion methods allow custom numeric-like objects to convert to built-in numeric types.
class Value:
def __int__(self):
return 10
def __float__(self):
return 10.5
def __complex__(self):
return 10.5 + 2jUsage:
v = Value()
print(int(v))
print(float(v))
print(complex(v))These conversions create built-in numeric objects.
They do not automatically make the custom type fully numeric. Arithmetic methods still need to be implemented separately.
17.25 Numeric Slots
Numeric behavior is implemented through slots.
Conceptual slot table:
PyNumberMethods
nb_add
nb_subtract
nb_multiply
nb_remainder
nb_divmod
nb_power
nb_negative
nb_positive
nb_absolute
nb_bool
nb_invert
nb_lshift
nb_rshift
nb_and
nb_xor
nb_or
nb_int
nb_float
nb_index
nb_matrix_multiply
nb_inplace_add
...Python syntax maps into this table.
| Syntax | Slot concept |
|---|---|
a + b | addition |
a - b | subtraction |
a * b | multiplication |
a @ b | matrix multiplication |
-a | negation |
abs(a) | absolute value |
bool(a) | truth value |
int(a) | integer conversion |
float(a) | float conversion |
a << b | left shift |
This slot table is why built-in and extension numeric types can integrate with Python syntax.
17.26 In-Place Numeric Operations
In-place operators include:
x += y
x -= y
x *= y
x //= y
x **= yFor immutable numbers, in-place operations create a new object and rebind the name.
x = 10
before = id(x)
x += 1
after = id(x)
print(before == after) # usually FalseFor mutable numeric-like objects, a type may implement true in-place mutation.
Built-in int, float, and complex are immutable.
17.27 Decimal and Fraction
The standard library provides numeric types outside the core built-ins.
decimal.Decimal provides decimal floating-point arithmetic.
from decimal import Decimal
print(Decimal("0.1") + Decimal("0.2"))fractions.Fraction provides rational arithmetic.
from fractions import Fraction
print(Fraction(1, 3) + Fraction(1, 6))These are Python-level library types with their own implementations and tradeoffs.
Use them when their semantics match the problem:
| Need | Type |
|---|---|
| General integers | int |
| Approximate scientific numeric work | float |
| Complex arithmetic | complex |
| Decimal financial arithmetic | Decimal |
| Exact rational arithmetic | Fraction |
17.28 C API: Creating Numbers
Creating an integer:
PyObject *x = PyLong_FromLong(42);
if (x == NULL) {
return NULL;
}Creating from a wider C type:
PyObject *x = PyLong_FromLongLong(value);Creating a float:
PyObject *f = PyFloat_FromDouble(3.14);
if (f == NULL) {
return NULL;
}Creating a complex:
PyObject *z = PyComplex_FromDoubles(1.0, 2.0);
if (z == NULL) {
return NULL;
}Each returns a new reference. The caller owns it.
17.29 C API: Extracting Numbers
Extracting a C long:
long value = PyLong_AsLong(obj);
if (value == -1 && PyErr_Occurred()) {
return NULL;
}The error check matters because -1 can be a valid result.
Extracting a double:
double value = PyFloat_AsDouble(obj);
if (value == -1.0 && PyErr_Occurred()) {
return NULL;
}Again, check the exception state.
For integer-like contexts, use index conversion APIs when exact integer semantics are required.
17.30 Overflow at C Boundaries
Python integers are arbitrary precision. C integers are fixed-width.
This conversion can fail:
long value = PyLong_AsLong(obj);if obj is too large for C long.
Python-level example:
x = 10 ** 100This value is valid as a Python int, but may not fit in any C integer type.
C extension code must handle overflow errors.
A common mistake is assuming Python int always fits into int, long, or size_t.
17.31 Performance Notes
Numeric performance depends on representation.
Small integer arithmetic is optimized but still operates on Python objects.
A tight loop:
total = 0
for i in range(10_000_000):
total += idoes many Python-level operations:
load total
load i
integer addition
create or retrieve result integer
store total
loop controlThis is much slower than equivalent C over raw machine integers.
For large numeric arrays, use specialized libraries that store raw values compactly, such as array, memoryview, or NumPy.
Python’s built-in numeric types optimize scalar semantics, not dense numeric vector computation.
17.32 Memory Notes
A list of integers stores references to integer objects.
xs = [1, 2, 3, 4]Conceptually:
list
[ptr][ptr][ptr][ptr]
| | | |
v v v v
int int int intAn array stores raw values:
from array import array
xs = array("i", [1, 2, 3, 4])Conceptually:
array
[int][int][int][int]This is why arrays are more memory-efficient for large homogeneous numeric data.
17.33 Common Numeric Pitfalls
| Pitfall | Example | Better approach |
|---|---|---|
Using is for numbers | x is 1000 | x == 1000 |
| Expecting decimal exactness from float | 0.1 + 0.2 == 0.3 | math.isclose or Decimal |
Persisting hash() | hash(x) as stable ID | hashlib |
| Assuming Python int fits C long | PyLong_AsLong unchecked | Check overflow |
| Using list for dense numeric arrays | [0] * n for huge numeric data | array or NumPy |
| Comparing NaN normally | nan == nan | Use math.isnan |
| Ignoring integer growth cost | huge powers in hot path | Consider algorithmic bounds |
17.34 Mental Model
Use this model:
int
immutable arbitrary-precision integer
variable-size digit array
no fixed overflow
cost grows with magnitude
bool
singleton subclass of int
truth-value type
values are True and False
float
immutable wrapper around C double
approximate binary floating-point
supports inf and nan
complex
immutable pair of doubles
real and imaginary parts
arithmetic supported
ordering unsupportedAt the CPython level:
numeric operation
dispatch through type slots
operate on concrete numeric representation
allocate or return result object
manage references17.35 Summary
CPython implements integers, floats, and complex numbers as Python objects with specialized C layouts. int uses arbitrary-precision storage, so it avoids fixed-width overflow but pays costs that grow with value size. float wraps a C double and inherits binary floating-point behavior. complex stores two doubles and supports complex arithmetic.
These numeric types are immutable. Operations create result objects rather than mutating operands. Their behavior is integrated through numeric slots, conversion protocols, hashing rules, and C API functions.