Embercode
✕
LoopsLesson 11 of 16

45 min read

Digit loops in Python

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.

Python · editable

Output:

2

The 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:

Python · editable

Output:

406.2
406

A 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 nn % 10 (last digit)n // 10 (what is left)
40622406
406640
4004
440

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.

Python · editable

Output:

digit 2
digit 6
digit 0
digit 4

Notice 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:

Python · editable

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.

Python · editable

Output:

sum_of_digits(4062) = 12
sum_of_digits(0) = 0
sum_of_digits(-907) = 16

The 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:

Python · editable

Output:

3 -91

The 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:

Passn beforetotal beforedigit (n % 10)n after (n // 10)total after
14062024062
240626408
3408048
4484012

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:

Python · editable

Output:

count_digit(1010, 0)=2
count_digit(5555, 5)=4
count_digit(0, 0)=1

This 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:

Python · editable

Output:

digital_root(9875)=2
digital_root(0)=0

The 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.

Python · editable

Trace 4062:

Passn beforeresult beforedigitresult after
14062022
24062626
340260260
4426042604

The result is 2604, which is 4062 read backwards.

Output of the three tests:

reverse(4062) = 2604
reverse(1200) = 21
reverse(1534236469) = 9646324351

The 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:

Python · editable

Output:

9876543210987654321

Some 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.

Python · editable

Output:

is_palindrome(1221) = True
is_palindrome(1220) = False
is_palindrome(-121) = False
is_palindrome(7) = True
is_palindrome(1000000001) = True

The 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:

Python · editable

Output:

True
False

This 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.

Python · editable

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)=False

The 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:

Passn beforedivisorfirstlastmatch?n afterdivisor after
11221100011yes2210
2221022yes00

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 total

Output for 4062:

10

The 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 / 10

Output for the first four passes:

2
6.199999999999989
0.6199999999999974
4.061999999999999

The 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:

KeyboardInterrupt

The 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:

Python · editable

Output:

-907 3 -91
-91 9 -10
-10 0 -1
-1 9 -1
-1 9 -1
-1 9 -1

The 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 count

The 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 largest

The 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, 2

The 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 with n //= 10. It stops when n reaches 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 abs for 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.
Practice this topic: coding problems for Loops →

Stuck? Ask the tutor

Ask about anything in this lesson. Answers are short and use simple words.

Sign in to ask the tutor.

← Sentinel loops with input in Python