Numbers: whole, decimal, and the one that surprises everybody
Two kinds of number, and five operators
Python has whole numbers (int) and decimals (float).
>>> 7 + 2 # 9
>>> 7 - 2 # 5
>>> 7 * 2 # 14
>>> 7 / 2 # 3.5
>>> 7 // 2 # 3
>>> 7 % 2 # 1
>>> 7 ** 2 # 49Three of those deserve attention.
/ always gives a float, even when it divides evenly. 10 / 5 is 2.0, not 2. This differs from most other languages and from Python 2, so old code and old tutorials get it wrong.
// is floor division — it rounds down, towards minus infinity, not towards zero. So 7 // 2 is 3, and -7 // 2 is -4, not -3. If you expected -3 you were thinking of truncation. This shows up when splitting negative quantities and it is genuinely confusing the first time.
% is the remainder, and it is more useful than it looks: n % 2 == 0 tests for even, n % 100 gives the last two digits, and seconds % 60 gives the seconds part of a duration.
Integers in Python have no size limit. 2 ** 1000 computes a 302-digit number without complaint, because Python stores big integers in as many chunks as they need. Most languages would overflow. It is slower than fixed-size arithmetic, and you will never notice unless you are doing cryptography.
The float surprise
Type this:
>>> 0.1 + 0.2
0.30000000000000004Python is not broken and this is not a bug in your machine. A float is a 64-bit IEEE 754 number: a sign, an exponent, and 53 bits of mantissa. Those bits store a value in binary, and 0.1 in binary is a recurring fraction, exactly the way 1/3 is recurring in decimal. It gets stored as the nearest representable value, which is very slightly off. Add two of those and the error becomes visible at the seventeenth digit.
Three consequences you have to live with:
- Never compare floats with
==.0.1 + 0.2 == 0.3isFalse. Usemath.isclose(a, b)instead, which allows a tiny tolerance. - Round only for display.
round(value, 2)gives you something to print. Keep the full value for further arithmetic, or you will round repeatedly and drift. - Never store money as a float. Store paise, cents or the smallest unit as integers, or use
decimal.Decimal("19.99"), which does base-10 arithmetic and is exact. Financial code that adds thousands of floats and compares the total against a bank statement will eventually be off by a paisa, and finding out why costs a day.
Rounding is not what you were taught
>>> round(2.5)
2
>>> round(3.5)
4That is not a mistake. Python uses banker's rounding: exact halves go to the nearest even number. Rounding 0.5 always up biases a long column of numbers upwards, and over a million rows that bias is measurable. Averaging the direction removes it.
If you need the school rule for a specific report, be explicit about it rather than fighting round:
from decimal import Decimal, ROUND_HALF_UP
Decimal("2.5").quantize(Decimal("1"), rounding=ROUND_HALF_UP) # 3Converting between them
int("42") # 42, from text
int(3.9) # 3 — truncates towards zero, it does not round
float("3.14") # 3.14
int("3.9") # ValueError: invalid literal for int() with base 10: '3.9'That last one catches people. int() will convert a float to an int, and will convert a string of digits to an int, but it will not do both steps at once. int(float("3.9")) works.
A useful habit
When a calculation gives an answer you did not expect, print the types before you print the values:
print(type(total), type(count), total, count)Half of all wrong arithmetic in a beginner's program is a string that looks like a number, and type() tells you in one line. The other half is integer division where you wanted /.
The float thing is not a Python quirk to be routed around. Every language using IEEE 754 doubles behaves identically, including JavaScript, Java, C and your spreadsheet. Excel hides it by displaying fewer digits; the error is still there.
The one thing to keep
`/` always returns a float and `//` rounds downwards, and 0.1 + 0.2 is not 0.3 because a float stores binary fractions with 53 bits of mantissa.
Before you move on
A stock program adds 0.1 to a total ten times and then tests `if total == 1.0:` to stop. It never stops. What is the mechanism, and what fixes it?
Pick the one you would defend. Nobody sees your answer.