Numeric types¶
Python has three built-in numeric types — int, float, and complex — and they cover almost everything. This notebook is about what each one is, how to write number literals clearly, and how the arithmetic operators behave (including the two or three that surprise people). The exact types Decimal and Fraction come in a later notebook; here we stay with the built-ins.
The three built-in types¶
int for whole numbers, float for numbers with a fractional part, complex for numbers with an imaginary component. Each has its own literal syntax, and type() tells you which you've got.
a = 42 # int
b = 3.14 # float
c = 2 + 3j # complex (j is the imaginary unit)
print(type(a), type(b), type(c))
print(c.real, c.imag) # complex numbers carry two floats
int never overflows¶
Unlike most languages, Python's int has no fixed size — it grows to fit whatever you compute. There's no 32- or 64-bit ceiling, no overflow, no wraparound. The only limit is your machine's memory.
print(2 ** 100) # a 31-digit number, no problem
big = 1
for n in range(1, 51):
big *= n # 50! — fifty factorial
print(big)
print(big.bit_length(), 'bits') # how many binary digits it takes
float, by contrast, is fixed-size (64-bit), which is the source of all the behaviour in the next notebook. For now, just note the asymmetry: integers are exact and unbounded; floats are approximate and bounded.
Writing number literals clearly¶
A few syntax features make numeric literals easier to read and write:
- Underscores group digits — Python ignores them, they're purely for humans.
- Prefixes write integers in other bases:
0xhex,0ooctal,0bbinary. - Scientific notation (
1e6) writes floats — note the result is always afloat.
population = 8_100_000_000 # underscores: same as 8100000000
print(population)
print(0xff, 0o17, 0b1010) # 255, 15, 10 — all written in other bases
print(1e6, type(1e6)) # 1000000.0 — scientific notation is a float
print(1.5e-3) # 0.0015
To go the other way — turn an int into its base representation as a string — use bin, hex, and oct. To parse a string in a given base, pass the base to int.
print(bin(10), hex(255), oct(15)) # '0b1010' '0xff' '0o17'
print(int('ff', 16), int('1010', 2)) # parse: 255, 10
The arithmetic operators¶
The usual suspects, plus three worth dwelling on: / always gives a float, // is floor division, and % is the remainder that pairs with it.
print(7 + 2, 7 - 2, 7 * 2) # 9 5 14
print(7 / 2) # 3.5 — true division ALWAYS returns a float
print(7 // 2) # 3 — floor division, rounds toward -inf
print(7 % 2) # 1 — remainder
print(2 ** 10) # 1024 — exponentiation
10 / 2 is 5.0, not 5 — / gives a float even when the result is whole. Reach for // when you want an integer result (indices, counts, page numbers).
print(10 / 2, type(10 / 2)) # 5.0 <class 'float'>
print(10 // 2, type(10 // 2)) # 5 <class 'int'>
Floor division and remainder with negatives¶
// rounds down (toward negative infinity), not toward zero — so -7 // 2 is -4, not -3. The % operator is defined to match, so the identity (a // b) * b + (a % b) == a always holds. A handy consequence: in Python a % b always has the same sign as b, which makes x % n reliable for wrapping into a range.
print(-7 // 2) # -4 (rounds toward negative infinity)
print(-7 % 2) # 1 (same sign as the divisor)
print(divmod(-7, 2)) # (-4, 1) — quotient and remainder together
# the identity that ties them together:
print((-7 // 2) * 2 + (-7 % 2)) # -7
divmod(a, b) returns (a // b, a % b) in one call — useful when you need both, like converting seconds into minutes-and-seconds.
total_seconds = 197
minutes, seconds = divmod(total_seconds, 60)
print(f'{minutes}m {seconds}s') # 3m 17s
Mixing types in one expression¶
When an expression mixes types, Python widens to the more general one, so you don't lose information: int + float → float. Combine an int and a complex and you get a complex. You rarely think about it, but it's why 1 + 2.0 is 3.0.
print(1 + 2.0) # 3.0 — int + float -> float
print(3 / 2) # 1.5 — float result
print((2 + 0j) + 3) # (5+0j) — anything + complex -> complex
bool is a kind of int¶
True and False are integers in disguise — True == 1 and False == 0, and they behave as such in arithmetic. This is occasionally a footgun, but more often a feature: summing a list of booleans counts how many are true.
print(True == 1, False == 0) # True True
print(True + True + False) # 2
print(isinstance(True, int)) # True — bool is a subclass of int
votes = [True, False, True, True, False]
print(sum(votes), 'yes') # 3 yes
Chained comparisons¶
Python lets you chain comparison operators the way maths notation does — 0 < x < 10 means exactly what it looks like, and each operand is evaluated once.
x = 7
print(0 < x < 10) # True
print(0 < x < 5) # False
# reads naturally for range checks
score = 85
print('pass' if 50 <= score <= 100 else 'fail')
The numeric built-ins¶
A handful of built-in functions cover the common operations without importing anything: abs, round, pow, min, max, and sum.
print(abs(-5), abs(3 - 7)) # 5 4
print(round(3.14159, 2)) # 3.14 — round to 2 decimal places
print(pow(2, 10)) # 1024 — same as 2 ** 10
print(pow(2, 10, 1000)) # 24 — (2**10) % 1000, computed efficiently
nums = [4, 1, 7, 3]
print(min(nums), max(nums), sum(nums)) # 1 7 15
round has a surprise hiding in it (round(2.5) is 2, not 3) and pow's three-argument form is genuinely useful for modular arithmetic — both are covered in the floating point notebook and the rounding recipe.
Recap¶
- Three built-in types:
int(exact, unbounded),float(approximate, 64-bit),complex. - Literals: underscores for grouping,
0x/0o/0bfor bases,1e6for floats. /always returns afloat;//floors toward negative infinity;%matches it;divmodgives both.- Mixed expressions widen to the more general type;
boolis anint.
Next: Floating point — why float arithmetic isn't quite what it seems, and what to do about it.