Numbers in Python: int, float, complex and bool
Integers are unbounded, floats are binary approximations, division always produces a float, and bool is quietly an integer. Each of those facts changes how you write arithmetic.
- Basics
- Data Types
- Operators
- Strings
- Control Flow
- Lists
- Tuples
- Sets
- Dictionaries
- Comprehensions
- Functions
- Advanced Functions
- Recursion
- Exception Handling
- File Handling
- Modules
- Standard Library
- OOP
- Advanced OOP
- Iterators and Generators
- Decorators
- Context Managers
- Descriptors and Dataclasses
- Python Internals
- Concurrency
- Regular Expressions
- Serialization
- Command Line Python
- Testing and Debugging
- Type Hints
- Performance
- Python Security
- DSA with Python
Integers have no ceiling
In most languages an integer occupies a fixed number of bits and overflows when it runs out. Python integers grow instead, limited only by available memory.
big = 2 ** 100
print(big) # 1267650600228229401496703205376
print(big * big) # exact, no overflow, no warningThis is convenient and it has a cost: arithmetic on very large integers is slower than on machine sized ones, because Python has to do the work in software. For ordinary values you will never notice.
Writing integer literals
decimal = 1_000_000 # underscores group digits, value unchanged
hexadecimal = 0xFF # 255
octal = 0o17 # 15
binary = 0b1011 # 11
print(bin(11), oct(15), hex(255)) # 0b1011 0o17 0xffFloats are approximations
A float is a 64 bit binary approximation of a decimal number. Most decimal fractions have no exact binary form, so small errors are unavoidable.
print(0.1 + 0.2) # 0.30000000000000004
print(1.1 * 3) # 3.3000000000000003
print(0.1 + 0.2 == 0.3) # FalseComparing floats properly
import math
print(math.isclose(0.1 + 0.2, 0.3)) # True
print(abs((0.1 + 0.2) - 0.3) < 1e-9) # True, the manual formSpecial float values
infinity = float("inf")
not_a_number = float("nan")
print(infinity > 10 ** 100) # True
print(not_a_number == not_a_number) # False. NaN equals nothing, including itself.
print(math.isnan(not_a_number)) # True, the correct testThe division operators
| Operator | Name | 7 and 2 | -7 and 2 | Result type |
|---|---|---|---|---|
/ | true division | 3.5 | -3.5 | always float |
// | floor division | 3 | -4 | int if both are int |
% | modulo | 1 | 1 | int if both are int |
** | power | 49 | -49* | depends on operands |
* -7 ** 2 is -49 because ** binds tighter than the minus sign. (-7) ** 2 is 49.
print(7 / 2) # 3.5 even though both operands are integers
print(7 // 2) # 3
print(-7 // 2) # -4 floor, not truncation towards zero
print(7 % 2) # 1
print(-7 % 2) # 1 the result carries the sign of the divisorFloor division rounds down, towards negative infinity. Many languages truncate towards zero instead. This is the single most common source of off by one bugs when translating code into Python.
divmod
quotient, remainder = divmod(17, 5)
print(quotient, remainder) # 3 2
# A practical use: seconds into minutes and seconds.
minutes, seconds = divmod(215, 60)
print(minutes, "min", seconds, "sec") # 3 min 35 secbool is an integer
print(True == 1) # True
print(False == 0) # True
print(True + True) # 2
print(isinstance(True, int)) # True
results = [92, 45, 78, 30, 66]
passed = sum(score >= 50 for score in results)
print(passed) # 3The last example is a genuinely idiomatic use: each comparison produces True or False, and sum adds them as 1 and 0.
Rounding
print(round(3.7)) # 4
print(round(3.14159, 2)) # 3.14
print(round(0.5)) # 0
print(round(1.5)) # 2
print(round(2.5)) # 2Python uses banker's rounding: an exact half rounds to the nearest even number. This keeps large sets of rounded values from drifting upwards. It surprises people the first time they meet it, and it is deliberate.
Useful built in numeric functions
print(abs(-9)) # 9
print(pow(2, 10)) # 1024
print(pow(2, 10, 1000)) # 24, that is (2 ** 10) % 1000 computed efficiently
print(min(4, 9, 2)) # 2
print(max([4, 9, 2])) # 9
print(sum([1, 2, 3])) # 6
print(int(9.99)) # 9, truncates towards zero
print(float(7)) # 7.0Augmented assignment
total = 10
total += 5 # 15
total -= 3 # 12
total *= 2 # 24
total /= 4 # 6.0 note the type changed to float
total //= 2 # 3.0
total **= 2 # 9.0
total %= 4 # 1.0Common mistakes
- Expecting
/to give an integer. It never does in Python 3. Use//. - Comparing floats with
==. - Assuming
-7 // 2is-3. It is-4. - Assuming
round(2.5)is3. - Using
floatfor currency. Usedecimal.Decimal, or store whole paise or cents as integers. - Writing
-2 ** 2and expecting4.
Best practices
- Use
//whenever an index or a count is wanted, so the result stays an integer. - Use
math.isclosefor every float comparison. - Use underscores in long numeric literals; they cost nothing and prevent misreadings.
- Keep money in integers of the smallest unit, or in
Decimal. Never infloat.
Practice
- Work out
17 // 5,-17 // 5,17 % 5and-17 % 5on paper, then verify each one. - Convert 4271 seconds into hours, minutes and seconds using
divmodtwice. - Explain why
round(0.5)andround(1.5)give different looking answers. - Count how many values in a list are negative, using a single
sumcall and noif. - Explain in two sentences why
0.1 + 0.2 != 0.3without using the word "bug".
Conclusion
Integers are exact and unbounded, floats are fast and approximate, / always yields a float, // floors rather than truncates, and bool is an int in disguise. Those five facts cover almost every numeric surprise Python has to offer.