Command Palette

Search for a command to run...

Hectal
PHASE 1Beginner ~26 min· topic 2 of 11

Topic 1.2

Whole Numbers: int

In one line

An int is a whole number, positive, negative or zero, with no size limit. Python adds, subtracts and multiplies it exactly, and has special operators for division with a quotient (//), a remainder (%) and powers (**).

Think of it like this

Counting marbles. You can have 0, 7 or 3,000 marbles, and you can owe a friend 2 (minus 2). You can never have 3.5 marbles in a whole-number count. The kind of number that counts whole things is the integer, written int in Python.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

int (integer)
A whole number with no decimal part and no size limit.
Literal
A value written directly in code, such as 42 or "hello".
Floor division (`//`)
Division that rounds the answer down to a whole number.
Remainder (modulo, `%`)
What is left over after dividing as many whole times as possible.
Power (`**`)
Multiply a number by itself several times: 2 ** 3 is 8.
Overflow
A number too big for its storage. Python's int never overflows.
Binary / hexadecimal
Ways to write numbers using base 2 (digits 0 and 1) or base 16 (digits 0-9 and a-f).
ZeroDivisionError
The error Python raises when you divide by zero.

Step by step

01Integers have no limit

A normal computer stores a number in a fixed number of bits (a bit is one tiny 0-or-1 switch), so there is a largest value. Python's int grows as needed: it keeps adding more storage for the digits. You can compute 2 ** 100, or multiply every number from 1 to 1000 together, without special tools.

The underscore in 1_400_000_000 is only for readers. It is the same number as 1400000000.

main.pywhole filepython
population = 1_400_000_000
print(population)       # 1400000000
print(2 ** 100)         # 1267650600228229401496703205376

02Other bases

People count in tens (base 10), computers think in twos (base 2), and programmers often use sixteen (base 16, hexadecimal) because one hex digit equals exactly four bits. Python lets you type numbers in any of these.

0b1010 is 8 + 2 = 10. 0o17 is 1 * 8 + 7 = 15. 0xFF is 15 * 16 + 15 = 255. Printing shows normal decimal numbers, because the value is the same whichever way you typed it.

main.pywhole filepython
print(0b1010, 0o17, 0xFF)               # 10 15 255
print(bin(10), oct(15), hex(255))       # 0b1010 0o17 0xff

03The four kinds of division arithmetic

Think of sharing 17 sweets among 5 children. Each child gets 17 // 5 = 3 sweets, and 17 % 5 = 2 sweets are left over. True division 17 / 5 = 3.4 answers a different question: how much is each share if the sweets could be cut.

main.pywhole filepython
print(17 / 5)       # 3.4  true division, always a float
print(17 // 5)      # 3    floor division
print(17 % 5)       # 2    remainder
print(2 ** 10)      # 1024 power
print(divmod(17, 5))  # (3, 2)
The four kinds of division arithmeticdiagram
Rendering diagram…

04Negative numbers and floor division

Floor division rounds toward minus infinity. On the number line, -3.4 lies between -4 and -3, and the floor is the one on the left: -4. So -17 // 5 is -4.

To keep (a // b) * b + a % b == a true, the remainder has to be -17 - (-4 * 5) = 3. That is why -17 % 5 is 3 in Python. In C and Java the remainder would be -2, which surprises people switching languages. The Python rule is more useful: % 7 always gives 0 to 6, even for negative numbers.

main.pywhole filepython
print(-17 // 5, -17 % 5)    # -4 3
print(divmod(-17, 5))       # (-4, 3)

05divmod splits a total into parts

To turn 135 minutes into hours and minutes, you need the quotient and the remainder of dividing by 60. divmod gives both in one call. Assigning the pair to two names (multiple assignment from Topic 1.1) makes it read like English.

main.pywhole filepython
total_minutes = 135
hours, minutes = divmod(total_minutes, 60)
print(hours, "h", minutes, "min")      # 2 h 15 min

06Remainders make cycles

A week has 7 days. If today is day 5 and you look 3 days ahead, the plain sum is 8, but there is no day 8. (5 + 3) % 7 wraps back to 1. Remainders turn endless counting into a loop that repeats. They appear in clocks, calendars and anything that wraps around to the start.

n % 2 == 0 tests whether n is even. You will use this idea often once you meet if in Phase 2.

07Dividing by zero

Zero cannot divide anything. /, // and % all raise ZeroDivisionError if the right side is zero. Python stops the program at that line and shows the traceback. The error message differs a little between operators, so read it.

terminal
$ python main.py
── expected output ──
Traceback (most recent call last):
File "main.py", line 3, in <module>
print(boxes // people)
~~~~~~^^~~~~~~~
ZeroDivisionError: integer division or modulo by zero

Try it yourself

  1. 1

    Find the hours and minutes

    Change total_minutes to 59, 60, 61 and 1000. Predict hours and minutes each time, then run.

  2. 2

    Floor towards minus infinity

    Predict -7 // 2 and -7 % 2 before you run them. Then check that (-7 // 2) * 2 + (-7 % 2) gives back -7.

  3. 3

    A very big number

    Print 2 ** 1000 and count its digits roughly. Python has no trouble. Think about why a fixed-size number could never hold it.

Code & diagrams

Big numbers, bases, division New tab

Change 17 and 5 to other numbers. Try a negative first number.

Sign in to run this example in your browser.

Expected output

1400000000
1267650600228229401496703205376
10 15 255
0b1010 0o17 0xff
3.4 3 2 1024
(3, 2)
-4 3
(-4, 3)
Splitting minutes and wrapping days New tab

The third line proves the rule that ties // and % together. type() tells you the kind of a value (Topic 1.6): 10 / 2 is a float, 10 // 2 is an int.

Sign in to run this example in your browser.

Expected output

2 h 15 min
True
day after 3 days: 1
3 -4 -4
<class 'float'> <class 'int'>
<class 'int'> <class 'float'>
How the number bases relatediagram
Rendering diagram…

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Divide by zero

Set people = 0 and run boxes // people.

main.pywhole filepython
boxes = 10
people = 0
print(boxes // people)
terminal
$ python main.py
── what you'll see ──
Traceback (most recent call last):
File "main.py", line 3, in <module>
print(boxes // people)
~~~~~~^^~~~~~~~
ZeroDivisionError: integer division or modulo by zero

Break #2

Zero in front of a number

Write a room number with a leading zero: 007.

main.pywhole filepython
room = 007
terminal
$ python main.py
── what you'll see ──
File "main.py", line 1
room = 007
^^
SyntaxError: leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers

Myth vs fact

Myth

/ and // do the same thing.

Fact

/ always returns a float (10 / 2 is 5.0). // returns the floored whole number. They differ even when the answer looks whole.

Myth

-17 % 5 should be -2, as in Java or C.

Fact

In Python the remainder takes the sign of the right operand, so -17 % 5 is 3. This keeps (a // b) * b + a % b == a true.

Myth

Integers overflow if they get too big.

Fact

Python's int grows as needed. Only memory is the limit. (NumPy's fixed-size integers do overflow: Phase 14.)

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    CPython stores an int as an array of 30-bit "digits", so big integers cost more time and memory the more digits they have. Multiplying two huge numbers uses Karatsuba, not the school method.

  • ▸

    Since the security fixes of Python 3.11 (also back-ported to older releases), converting a huge int to a decimal string, or the other way round, is limited to 4300 digits by default, to stop denial-of-service attacks on slow conversions (the cost grows with the square of the length). str(10 ** 4300) and int("9" * 5000) raise ValueError: Exceeds the limit (4300 digits) for integer string conversion. sys.set_int_max_str_digits() or the PYTHONINTMAXSTRDIGITS environment variable changes the limit (0 removes it). Only bases that are not a power of two are limited, so hexadecimal and binary conversions are not.

  • ▸

    Two more facts interviewers like. int.bit_length() gives the bits needed ((255).bit_length() is 8), and the power-of-two test n > 0 and n & (n - 1) == 0 works as written in Python, because comparisons bind looser than & (in C the same line would group the other way and be a bug; Topic 1.7). Bit operators on negative ints behave as if the number had infinitely many leading sign bits (two's complement), so -1 & 0xFF is 255 and ~5 is -6. And // and % on floats work too and follow the same floor rule: -7.5 // 2 is -4.0.

  • ▸

    For exactness with big numbers Python is a good choice: cryptography teaching code and combinatorics both rely on unlimited ints. For speed on millions of numbers use NumPy (Phase 14), but remember its integers have a fixed size and do overflow.

  • ▸

    The % operator is how hash tables and ring buffers pick a slot. The same idea appears in the DSA course (/dsa) in hashing and circular queues.

Remember this

  1. 1

    Python's int has no size limit except your computer's memory. 2 ** 100 is a 31-digit number, and Python prints it exactly. In many other languages the number would overflow: Java's int silently wraps around, and C's signed int is undefined behaviour. This is one reason Python is pleasant for number work.

  2. 2

    You can write big numbers with underscores for readability: 1_400_000_000. Python ignores the underscores. You can also write whole numbers in other bases: binary with 0b1010, octal with 0o17, and hexadecimal with 0xFF. They are all the same kind of int; only the way you type them differs. bin(), oct() and hex() turn an int into text in those bases.

  3. 3

    Python has two kinds of division. / always gives a float (a decimal number): 17 / 5 is 3.4. // is floor division: it gives the whole-number quotient, 17 // 5 is 3. % is the remainder (the modulo): 17 % 5 is 2. ** is the power: 2 ** 10 is 1024.

  4. 4

    Floor division always rounds down toward minus infinity, not toward zero. So -17 // 5 is -4, not -3. The remainder follows the matching rule: it takes the sign of the divisor, so -17 % 5 is 3. The two always agree: (a // b) * b + a % b == a.

  5. 5

    divmod(a, b) returns both results at once, as a pair (quotient, remainder). It is the natural tool for splitting a total into parts: 135 minutes is divmod(135, 60), which is 2 hours and 15 minutes.

  6. 6

    The remainder is the key to anything that repeats in a cycle: days of the week (% 7), clock hours (% 12), even or odd (% 2), wrapping an index around a list. Dividing by zero with //, % or / is an error: ZeroDivisionError.

Explain it without notes

01

What is the difference between /, // and %? Give an example for each.

02

Why is -17 // 5 equal to -4, and what is -17 % 5?

03

What does it mean that Python integers are unlimited, and what is the trade-off?

04

How would you turn 3725 seconds into hours, minutes and seconds?

Practice

01

A shop sells pens in packs of 12. You have 100 pens. Print how many full packs you can make and how many pens are left over.

02

Print the last digit of 4729 and the number with its last digit removed, using % and //.

03

A clock shows hour 10. Print the hour on the clock 5 hours later, on a 12-hour clock where the hours are 0 to 11.

Trade-offs

  • ↔

    Unlimited precision vs. speed. Python never overflows, which removes a whole family of bugs, but each operation on big ints is slower than a machine instruction.

  • ↔

    Floor division vs. truncation. Python's // rounds down, which makes % always positive for a positive divisor and is better for cycles. Engineers coming from C must remember that the result for negatives is different.

Done when you can

  • Done when you can write a large number with underscores and a number in binary or hex.

  • Done when you can say what /, //, % and ** each return, with an example.

  • Done when you can predict -17 // 5 and -17 % 5.

  • Done when you can use divmod to split minutes into hours and minutes.

  • Done when you can read a ZeroDivisionError traceback.