Why this matters
Many number problems ask about the digits of a number, not the number as a whole. A check digit on a card number is built from its digits. A puzzle asks whether a year reads the same from both ends. A program that prints a number backwards has to put its digits in a new order.
A digit is one of the ten symbols 0 to 9. The number 4062 has four digits: 4, 0, 6 and 2. You can see them on the screen, but Python stores 4062 as one value. To work with the digits, the program has to pull them out one at a time.
This lesson shows how. It uses two operators you may already know. The % operator gives the remainder after a division. The // operator, called floor division, gives the whole part of a division, rounded down. A third operator, /, always gives a decimal number, and the mistakes section shows why that matters here. The loop is the same pattern in every example, so once you know it, the three tasks in this lesson are small changes to one loop. The loop itself is a while loop, which the earlier lessons of this module cover.
The idea: take off one digit at a time
Getting the last digit with %
Start with a small question. What is the last digit of 4062?
Divide 4062 by 10. The answer is 406 with 2 left over. The leftover part is 2, and it is the last digit. In Python, the % operator gives that leftover part, called the remainder.
Output:
2The same is true for any whole number. The remainder after dividing by 10 is always the last digit.
Removing the last digit with //
Now remove that last digit. The // operator divides and keeps only the whole part. So 4062 // 10 is 406. The 2 has gone.
The single / operator also divides, but it keeps the decimal part. Its result is a float, which is a number with a decimal point:
Output:
406.2
406A digit loop needs whole numbers, so it uses //.
Repeat the two steps. Each row of the table below is one pass of the loop:
| Value of n | n % 10 (last digit) | n // 10 (what is left) |
|---|---|---|
| 4062 | 2 | 406 |
| 406 | 6 | 40 |
| 40 | 0 | 4 |
| 4 | 4 | 0 |
When the value reaches 0, every digit has been taken off. That is the stop signal for the loop.
The loop pattern
Put the two steps inside a while loop. The loop keeps going while n is greater than 0.
Output:
digit 2
digit 6
digit 0
digit 4Notice the order. The digits come out last first. Each pass sees the rightmost digit that is left. Some tasks, like summing, do not care about order. Others, like reversing, depend on it.
The loop does three things on each pass: it reads the last digit, uses it, and removes it. If you forget the third step, n never shrinks, the condition stays true, and the loop never ends. The mistakes section shows this.
Python has a built-in function that does the first two steps in one call. divmod(n, 10) returns the quotient, which is the result of //, and the remainder, which is the result of %, as a pair:
Output:
(406, 2)The line n, digit = divmod(n, 10) puts the two parts into two names at once. Either form works. This lesson uses % and // because they show each step on its own line.
The number of passes depends on how many digits the number has, not on how big it is. Each pass removes one digit, so a number with four digits takes four passes. A ten-digit number takes ten passes. The loop does about one pass per digit, and the work stays small even for large values.
Worked example 1: summing the digits
The digit sum of a number is the total of all its digits. The digit sum of 4062 is 4 + 0 + 6 + 2, which is 12.
Here is a function that returns the digit sum. It uses a variable called total that starts at 0 and gets one digit added on each pass. A function is declared with def.
Output:
sum_of_digits(4062) = 12
sum_of_digits(0) = 0
sum_of_digits(-907) = 16The line n = abs(n) replaces n with its absolute value. abs turns -907 into 907. Without it, the condition n > 0 would be false for every negative number, and the function would return 0. The digits of -907 are the same as the digits of 907, so the sign should not change the answer.
Negative numbers also change what % and // return in Python. Python rounds // down, so a negative number gives a result one step further from zero than you might expect:
Output:
3 -91The last digit of -907 is not 7 when you use these operators, so abs must come first.
Trace the function on 4062 by writing down the state after each pass:
| Pass | n before | total before | digit (n % 10) | n after (n // 10) | total after |
|---|---|---|---|---|---|
| 1 | 4062 | 0 | 2 | 406 | 2 |
| 2 | 406 | 2 | 6 | 40 | 8 |
| 3 | 40 | 8 | 0 | 4 | 8 |
| 4 | 4 | 8 | 4 | 0 | 12 |
The loop ends because n is 0. The function returns 12.
The result for 0 is 0 because the loop does not run at all. The number 0 has no digits that the loop would take off, and its digit sum is 0 anyway.
Worked example 2: counting one digit
The second example is a little harder. It counts how many times a chosen digit appears in a number. For example, the digit 0 appears twice in 1010.
The function is the same loop with one extra test on each pass:
Output:
count_digit(1010, 0)=2
count_digit(5555, 5)=4
count_digit(0, 0)=1This version uses while True with a break at the end of the body. Python has no loop that checks its condition after the body, which Java calls a do-while loop. The while True form does the same job. The body runs once before the check, so the number 0, which has one digit, is counted. A plain while n > 0 loop would skip the body for 0 and find no digits at all.
The function counts a digit that is found on any pass. It does not care about position, so the backwards order of the digits does not change the result.
Worked example 3: the digital root
The third example uses a loop inside a loop. The digital root of a number is found by adding its digits. If the total has more than one digit, add those digits too. Keep going until one digit is left.
For 9875 the steps are: 9 + 8 + 7 + 5 = 29, then 2 + 9 = 11, then 1 + 1 = 2. So the digital root of 9875 is 2.
The outer loop repeats while the number has two or more digits. The inner loop is the digit sum from example 1, run on the current number:
Output:
digital_root(9875)=2
digital_root(0)=0The inner loop changes n down to 0. Then n = total puts the new total back into n, so the outer condition can check it again. Each pass of the outer loop makes the number smaller, so the outer loop always ends.
Read the code once more and notice what the inner loop uses. It does not need to know that it is inside another loop. It takes whatever n is and reduces it. This is a common way to build bigger loops out of smaller ones that already work. The function from example 1 could also be called here in place of the inner loop. The inner loop is written out so that the nesting is easy to see.
Reversing a number
To reverse a number, build a new number out of the digits in the opposite order. The digits come out of the loop last first, which is exactly the order a reversed number needs. Each new digit goes on the right of the result.
Multiplying by 10 moves a number's digits one place to the left, leaving a 0 on the right. So the rule is: multiply the result by 10, then add the new digit.
Trace 4062:
| Pass | n before | result before | digit | result after |
|---|---|---|---|---|
| 1 | 4062 | 0 | 2 | 2 |
| 2 | 406 | 2 | 6 | 26 |
| 3 | 40 | 26 | 0 | 260 |
| 4 | 4 | 260 | 4 | 2604 |
The result is 2604, which is 4062 read backwards.
Output of the three tests:
reverse(4062) = 2604
reverse(1200) = 21
reverse(1534236469) = 9646324351The second line surprises many beginners. The reversed number is 21, not 0021. A number does not keep leading zeros, so the zeros at the start of the reversed digits disappear. Exercise 4 at the end of this lesson shows how to keep them.
A Python int has no fixed size. It holds as many digits as the computer's memory allows, so a large reversal is correct:
Output:
9876543210987654321Some languages store a whole number in a fixed amount of space. When a result is too big for that space, it wraps around to a wrong value, and no error is shown. Python does not have that problem for whole numbers.
Palindromes
A palindrome is a number that reads the same forwards and backwards. 1221 is a palindrome. 1220 is not, because it reads 0221 backwards.
The simplest check is to reverse the number and compare the result with the original. If they match, it is a palindrome.
Output:
is_palindrome(1221) = True
is_palindrome(1220) = False
is_palindrome(-121) = False
is_palindrome(7) = True
is_palindrome(1000000001) = TrueThe check n < 0 comes first because a negative number has a minus sign at the front. Written backwards, -121 would end in a minus sign, so it can never match. The reverse function returns 0 for a negative number, because the loop condition n > 0 is false. The check is still worth writing, because it states the rule directly.
Python also lets you compare the text of the number. str(n) turns the number into text. The [::-1] part is called slicing. It makes a copy of the text that runs from the last character to the first:
Output:
True
FalseThis is the shortest form, and it is a good check when you want to test your loop. It does not use a digit loop, though, so it does not teach the idea of this lesson.
A second method: compare the two ends
The reverse function is simple, but it builds a new number. A second method checks a palindrome without building the reversed number. It compares the first digit with the last digit, then the second with the second-last, and so on, moving inwards.
To read the first digit, the program needs a divisor, which is a power of 10 with the same number of digits as the input. For 1221 the divisor is 1000, because 1221 // 1000 is 1, the first digit.
Output:
pal(1221)=True
pal(1220)=False
pal(7)=True
pal(0)=True
pal(10)=False
pal(12321)=True
pal(1001)=True
pal(100)=False
pal(1000000001)=True
pal(123454321)=True
pal(-121)=FalseThe first loop finds the divisor. It multiplies the divisor by 10 until the number divided by the divisor is less than 10. Then the divisor is the place value of the first digit.
The second loop does the comparison. Each pass checks the outside pair of digits. Then it strips both of them away: n % divisor removes the first digit, and // 10 removes the last digit. The divisor drops by 100 because two digits are gone.
Trace 1221 through the second loop:
| Pass | n before | divisor | first | last | match? | n after | divisor after |
|---|---|---|---|---|---|---|---|
| 1 | 1221 | 1000 | 1 | 1 | yes | 22 | 10 |
| 2 | 22 | 10 | 2 | 2 | yes | 0 | 0 |
The loop ends with n at 0, so the function returns True.
Both methods give the same answers on the test cases above. The first one is easier to read, so it is the one to use first. The second one shows how a digit loop can work from both ends at once, which is useful in harder problems.
Common mistakes
Reading the digit after dividing
The loop must read the digit before it divides n. If the order is swapped, the program skips a digit.
def wrong_order_sum(n):
total = 0
while n > 0:
n = n // 10
total += n % 10
return totalOutput for 4062:
10The correct answer is 12. The first pass divides 4062 to 406 and then reads 6, so the 2 is never added. Reading the digit first, then dividing, fixes it.
Using / instead of //
The / operator keeps the decimal part, so n stops being a whole number after the first pass:
n = 4062
while n > 0:
digit = n % 10
print(digit)
n = n / 10Output for the first four passes:
2
6.199999999999989
0.6199999999999974
4.061999999999999The first pass is right, because 4062 is still a whole number. After that, n is 406.2, and n % 10 gives a decimal such as 6.199999999999989 instead of 6. The loop only stopped after 328 passes, when n had shrunk to 0.0 after many divisions. The printed digits were wrong from the second pass on. The fix is to use n = n // 10.
Forgetting to change n
Every digit loop needs a step that makes n smaller. If the body reads the digit but never runs n //= 10, the value of n stays the same and the loop never stops.
n = 4062
while n > 0:
digit = n % 10
print(digit)This program prints 2 forever. To stop it, press Ctrl+C in the terminal. Python stops the program and prints the last line of its error message:
KeyboardInterruptThe fix is to add n //= 10 as the last step of the body.
Forgetting abs for negative numbers
Without abs, a loop that tests n != 0 never ends for a negative number:
def sum_no_abs(n):
total = 0
while n != 0:
total += n % 10
n //= 10
return total
print(sum_no_abs(-907))This short loop prints the three values for six passes, which is enough to show why:
Output:
-907 3 -91
-91 9 -10
-10 0 -1
-1 9 -1
-1 9 -1
-1 9 -1The value -1 divided by 10 and rounded down is still -1, so n never reaches 0. The loop runs forever. The version in this lesson uses > 0 and abs, which avoids this.
Passing text instead of a number
The function input always returns text, even when the user types digits. Text cannot be used in the arithmetic of a digit loop:
text = "4062"
print(sum_of_digits(text))The last line of the error message is:
TypeError: bad operand type for abs(): 'str'The type name str means text. The fix is to turn the text into a whole number first with int(text).
Practice
Exercise 1. What does sum_of_digits(305) return? Work it out by hand, then check it.
Show the answer
It returns 8. The digits are 3, 0 and 5, and 3 + 0 + 5 = 8.
Exercise 2. Write a function count_even_digits(n) that counts how many of the digits of n are even. Treat 0 as even. Use a loop that reads each digit.
Show the answer
def count_even_digits(n):
count = 0
n = abs(n)
while True:
if n % 2 == 0:
count += 1
n //= 10
if n == 0:
break
return countThe results are 4 for 2468, 0 for 13579, 1 for 0 and 2 for -120. The while True form makes 0 count as one digit.
Exercise 3. Write a function largest_digit(n) that returns the biggest digit of n. For example, largest_digit(305) returns 5.
Show the answer
def largest_digit(n):
largest = 0
n = abs(n)
while True:
largest = max(largest, n % 10)
n //= 10
if n == 0:
break
return largestThe function starts with largest at 0 and keeps the bigger value on each pass. largest_digit(0) returns 0.
Exercise 4. The reverse of 1200 is 21, not 0021. Explain in plain words why the zeros disappear, and what would be needed to keep them.
Show the answer
A number holds a value, not a set of symbols. The value 21 is the same as 021 or 0021, so the zeros at the front have no meaning as a number. The loop builds the result as a number, so it cannot keep them. To keep the zeros, work with text. The expression str(1200)[::-1] gives the text 0021, and int would turn it back into 21.
Exercise 5. The digital root of 9875 is 2. Write a function is_single_digit(n) that returns True when n has one digit, then use it in a loop to print each step of the digital root of 9875. Print the steps as 9875, 29, 11, 2.
Show the answer
def is_single_digit(n):
return abs(n) < 10
def print_digital_root_steps(n):
steps = [str(n)]
while not is_single_digit(n):
total = 0
rest = abs(n)
while rest > 0:
total += rest % 10
rest //= 10
n = total
steps.append(str(n))
print(", ".join(steps))
print_digital_root_steps(9875)Output:
9875, 29, 11, 2The inner loop uses a copy called rest, so n does not change until the new total is ready. The steps list collects the values as text, so the print is a single line.
Summary
- A digit loop reads the last digit with
n % 10, uses it, then removes it withn //= 10. It stops whennreaches 0. - The digits come out last first, which is why reversing a number works with this loop.
- To sum or count digits, add a line to the loop body. To reverse, build a new number by multiplying by 10 and adding each digit.
- Use
absfor negative input. Use//, not/, so the value stays a whole number. - A palindrome check can reverse the number, compare the text with slicing, or compare the outer digits from both ends.