Digits and Number Manipulation
The loop everyone writes first
Reverse Integer asks for the digits of a 32-bit integer in reverse order, or 0 when the reverse doesn't fit in 32 bits. Here's the loop almost everyone writes first, run on 123 and on 1,534,236,469:
#include <bits/stdc++.h>
using namespace std;
int reverse(int x) { // the loop everyone writes first
int result = 0;
while (x != 0) {
result = result * 10 + x % 10;
x /= 10;
}
return result;
}
int main() {
printf("%d %d\n", reverse(123), reverse(1534236469));
}
// 321 1056389759def reverse(x): # the loop everyone writes first
sign = -1 if x < 0 else 1
x = abs(x)
result = 0
while x != 0:
result = result * 10 + x % 10
x //= 10
return sign * result
print(reverse(123), reverse(1534236469))
# 321 9646324351public class Main {
static int reverse(int x) { // the loop everyone writes first
int result = 0;
while (x != 0) {
result = result * 10 + x % 10;
x /= 10;
}
return result;
}
public static void main(String[] args) {
System.out.println(reverse(123) + " " + reverse(1534236469));
}
}
// 321 1056389759function reverse(x) { // the loop everyone writes first
let result = 0;
while (x !== 0) {
result = result * 10 + x % 10;
x = Math.trunc(x / 10);
}
return result;
}
console.log(reverse(123), reverse(1534236469));
// 321 9646324351On 123 all four agree: 321. On 1,534,236,469 they don't. C++ and Java printed 1,056,389,759: the true reverse, 9,646,324,351, is bigger than the largest 32-bit integer, 2,147,483,647, so the int wrapped around past 2³². In Java that wrap is defined; in C++ it's undefined behaviour, so not even that number is promised. Python and JavaScript printed 9,646,324,351, because their numbers don't stop at 32 bits. The answer the problem wants is 0, and none of them gave it.
Every problem in this lesson is that loop. % 10 takes the last digit off, / 10 drops it, and × 10 + digit puts one on. What changes is one decision: the sign, the overflow, the base, or a carry. This lesson takes them in turn, then multiplies numbers too long for any integer type, digit by digit.

Taking a number apart
n % 10 is the last digit of n, and n / 10 (whole-number division) is n without it. Repeat until nothing is left and the digits come off right to left, one per pass:
#include <bits/stdc++.h>
using namespace std;
vector<int> digitsOff(long long n) { // the digits of n >= 0, last one first
vector<int> out;
do {
out.push_back(n % 10); // the last digit
n /= 10; // drop it
} while (n != 0);
return out;
}
int main() {
for (long long n : {4837LL, 0LL, 1000000LL}) {
vector<int> d = digitsOff(n);
int sum = 0;
printf("%lld:", n);
for (int x : d) {
printf(" %d", x);
sum += x;
}
printf(" off the right, count %zu, sum %d\n", d.size(), sum);
}
}
// 4837: 7 3 8 4 off the right, count 4, sum 22
// 0: 0 off the right, count 1, sum 0
// 1000000: 0 0 0 0 0 0 1 off the right, count 7, sum 1def digits_off(n): # the digits of n >= 0, last one first
out = []
while True:
out.append(n % 10) # the last digit
n //= 10 # drop it
if n == 0:
return out
for n in (4837, 0, 1000000):
d = digits_off(n)
print(f"{n}: {' '.join(map(str, d))} off the right, count {len(d)}, sum {sum(d)}")
# 4837: 7 3 8 4 off the right, count 4, sum 22
# 0: 0 off the right, count 1, sum 0
# 1000000: 0 0 0 0 0 0 1 off the right, count 7, sum 1import java.util.*;
public class Main {
static List<Integer> digitsOff(long n) { // the digits of n >= 0, last one first
List<Integer> out = new ArrayList<>();
do {
out.add((int) (n % 10)); // the last digit
n /= 10; // drop it
} while (n != 0);
return out;
}
public static void main(String[] args) {
for (long n : new long[]{4837, 0, 1000000}) {
List<Integer> d = digitsOff(n);
int sum = 0;
StringBuilder line = new StringBuilder(n + ":");
for (int x : d) {
line.append(" ").append(x);
sum += x;
}
System.out.println(line + " off the right, count " + d.size() + ", sum " + sum);
}
}
}
// 4837: 7 3 8 4 off the right, count 4, sum 22
// 0: 0 off the right, count 1, sum 0
// 1000000: 0 0 0 0 0 0 1 off the right, count 7, sum 1function digitsOff(n) { // the digits of n >= 0, last one first
const out = [];
do {
out.push(n % 10); // the last digit
n = Math.floor(n / 10); // drop it
} while (n !== 0);
return out;
}
for (const n of [4837, 0, 1000000]) {
const d = digitsOff(n);
const sum = d.reduce((a, b) => a + b, 0);
console.log(`${n}: ${d.join(" ")} off the right, count ${d.length}, sum ${sum}`);
}
// 4837: 7 3 8 4 off the right, count 4, sum 22
// 0: 0 off the right, count 1, sum 0
// 1000000: 0 0 0 0 0 0 1 off the right, count 7, sum 14,837 gave 7, 3, 8 and 4, in four passes. A number with d digits takes d passes, and n has ⌊log₁₀ n⌋ + 1 digits, so the loop is O(log n): at most 10 passes for a 32-bit number and 19 for a 64-bit one.
The loop divides first and tests after, so 0 gets one pass and one digit. Testing first (while n > 0) never enters the loop for 0 and counts no digits, the most common slip in Count Digits of a Number.
Armstrong numbers use the loop to count the digits, then add up each digit raised to that count. 153 is one: 1³ + 5³ + 3³ = 153. Checking every number below a million:
#include <bits/stdc++.h>
using namespace std;
bool isArmstrong(long long n) { // the sum of its digits, each to the power of how many there are
vector<int> digits;
long long m = n;
do {
digits.push_back(m % 10);
m /= 10;
} while (m != 0);
long long total = 0;
for (int d : digits) {
long long power = 1;
for (size_t k = 0; k < digits.size(); k++) power *= d; // whole numbers only, no pow()
total += power;
}
return total == n;
}
int main() {
vector<long long> found;
for (long long n = 1; n < 1000000; n++)
if (isArmstrong(n)) found.push_back(n);
printf("%zu below 1,000,000:", found.size());
for (long long n : found) printf(" %lld", n);
printf("\n");
}
// 20 below 1,000,000: 1 2 3 4 5 6 7 8 9 153 370 371 407 1634 8208 9474 54748 92727 93084 548834def is_armstrong(n): # the sum of its digits, each to the power of how many there are
digits = []
m = n
while True:
digits.append(m % 10)
m //= 10
if m == 0:
break
k = len(digits)
return sum(d ** k for d in digits) == n
found = [n for n in range(1, 1000000) if is_armstrong(n)]
print(f"{len(found)} below 1,000,000:", " ".join(map(str, found)))
# 20 below 1,000,000: 1 2 3 4 5 6 7 8 9 153 370 371 407 1634 8208 9474 54748 92727 93084 548834import java.util.*;
public class Main {
static boolean isArmstrong(long n) { // the sum of its digits, each to the power of how many there are
List<Integer> digits = new ArrayList<>();
long m = n;
do {
digits.add((int) (m % 10));
m /= 10;
} while (m != 0);
long total = 0;
for (int d : digits) {
long power = 1;
for (int k = 0; k < digits.size(); k++) power *= d; // whole numbers only, no Math.pow
total += power;
}
return total == n;
}
public static void main(String[] args) {
StringBuilder line = new StringBuilder();
int count = 0;
for (long n = 1; n < 1000000; n++)
if (isArmstrong(n)) {
line.append(" ").append(n);
count++;
}
System.out.println(count + " below 1,000,000:" + line);
}
}
// 20 below 1,000,000: 1 2 3 4 5 6 7 8 9 153 370 371 407 1634 8208 9474 54748 92727 93084 548834function isArmstrong(n) { // the sum of its digits, each to the power of how many there are
const digits = [];
let m = n;
do {
digits.push(m % 10);
m = Math.floor(m / 10);
} while (m !== 0);
let total = 0;
for (const d of digits) {
let power = 1;
for (let k = 0; k < digits.length; k++) power *= d; // whole numbers only, no Math.pow
total += power;
}
return total === n;
}
const found = [];
for (let n = 1; n < 1000000; n++) if (isArmstrong(n)) found.push(n);
console.log(`${found.length} below 1,000,000: ${found.join(" ")}`);
// 20 below 1,000,000: 1 2 3 4 5 6 7 8 9 153 370 371 407 1634 8208 9474 54748 92727 93084 548834There are 20 below a million, the largest 548,834. Each power is built by multiplying whole numbers, so no floating-point rounding is involved.
Negative numbers: each language rounds its own way
For a negative number the two operations disagree between languages. C++, Java and JavaScript round the quotient toward zero and give the remainder the dividend's sign; Python rounds down and gives it the divisor's sign:
#include <bits/stdc++.h>
using namespace std;
int main() {
printf("-123 %% 10 = %d\n", -123 % 10); // the remainder takes the dividend's sign
printf("-123 / 10 = %d\n", -123 / 10); // the quotient rounds toward zero
}
// -123 % 10 = -3
// -123 / 10 = -12print("-123 % 10 =", -123 % 10) # the remainder takes the divisor's sign
print("-123 // 10 =", -123 // 10) # the quotient rounds down
print("int(-123 / 10) =", int(-123 / 10)) # true division, then toward zero
# -123 % 10 = 7
# -123 // 10 = -13
# int(-123 / 10) = -12public class Main {
public static void main(String[] args) {
System.out.println("-123 % 10 = " + (-123 % 10)); // the remainder takes the dividend's sign
System.out.println("-123 / 10 = " + (-123 / 10)); // the quotient rounds toward zero
System.out.println("Math.abs(-2147483648) = " + Math.abs(-2147483648));
}
}
// -123 % 10 = -3
// -123 / 10 = -12
// Math.abs(-2147483648) = -2147483648console.log("-123 % 10 =", -123 % 10); // the dividend's sign
console.log("Math.trunc(-123 / 10) =", Math.trunc(-123 / 10)); // toward zero
console.log("Math.floor(-123 / 10) =", Math.floor(-123 / 10)); // down
// -123 % 10 = -3
// Math.trunc(-123 / 10) = -12
// Math.floor(-123 / 10) = -13| Language | −123 % 10 | −123 divided by 10 |
|---|---|---|
| C++ | −3 | −12 with / (toward zero) |
| Python | 7 | −13 with // (down), −12 with int(-123 / 10) |
| Java | −3 | −12 with / (toward zero) |
| JavaScript | −3 | −12 with Math.trunc, −13 with Math.floor |
Both rules keep (n / 10) × 10 + n % 10 equal to n; they split n differently. So a digit loop ported from Java to Python finds 7 as the last digit of −123, and one ported the other way finds −3. The habit that works everywhere: take the sign off, work on the size, put the sign back.
Taking the sign off has a trap of its own in 32 bits: the smallest int, −2,147,483,648, has no positive partner. Java's Math.abs of it printed −2,147,483,648, still negative, and in C++ it's undefined behaviour. That's why the 32-bit reverse below keeps the sign in its digits instead.
Counting digits with logarithms
On paper n ≥ 1 has ⌊log₁₀ n⌋ + 1 digits, and without a log₁₀ at hand people write it as log(n) / log(10). Computed in a double, both go wrong:
#include <bits/stdc++.h>
using namespace std;
int countDigits(long long n) { // the loop: always right
int count = 0;
do {
count++;
n /= 10;
} while (n != 0);
return count;
}
int main() {
for (long long n : {1000LL, 999999999999999LL}) {
int byLog10 = (int) floor(log10((double) n)) + 1;
int byLn = (int) floor(log((double) n) / log(10.0)) + 1;
printf("%lld: loop %d, log10 %d, log(n)/log(10) %d\n", n, countDigits(n), byLog10, byLn);
}
}
// 1000: loop 4, log10 4, log(n)/log(10) 3
// 999999999999999: loop 15, log10 16, log(n)/log(10) 15import math
def count_digits(n): # the loop: always right
count = 0
while True:
count += 1
n //= 10
if n == 0:
return count
for n in (1000, 999999999999999):
by_log10 = math.floor(math.log10(n)) + 1
by_ln = math.floor(math.log(n) / math.log(10)) + 1
print(f"{n}: loop {count_digits(n)}, log10 {by_log10}, log(n)/log(10) {by_ln}")
# 1000: loop 4, log10 4, log(n)/log(10) 3
# 999999999999999: loop 15, log10 16, log(n)/log(10) 15public class Main {
static int countDigits(long n) { // the loop: always right
int count = 0;
do {
count++;
n /= 10;
} while (n != 0);
return count;
}
public static void main(String[] args) {
for (long n : new long[]{1000L, 999999999999999L}) {
int byLog10 = (int) Math.floor(Math.log10(n)) + 1;
int byLn = (int) Math.floor(Math.log(n) / Math.log(10)) + 1;
System.out.println(n + ": loop " + countDigits(n) + ", log10 " + byLog10 + ", log(n)/log(10) " + byLn);
}
}
}
// 1000: loop 4, log10 4, log(n)/log(10) 3
// 999999999999999: loop 15, log10 16, log(n)/log(10) 15function countDigits(n) { // the loop: always right
let count = 0;
do {
count++;
n = Math.floor(n / 10);
} while (n !== 0);
return count;
}
for (const n of [1000, 999999999999999]) {
const byLog10 = Math.floor(Math.log10(n)) + 1;
const byLn = Math.floor(Math.log(n) / Math.log(10)) + 1;
console.log(`${n}: loop ${countDigits(n)}, log10 ${byLog10}, log(n)/log(10) ${byLn}`);
}
// 1000: loop 4, log10 4, log(n)/log(10) 3
// 999999999999999: loop 15, log10 16, log(n)/log(10) 15log(1,000) / log(10) came out just under 3, so the second formula said 1,000 has 3 digits. And log₁₀(999,999,999,999,999) rounded to exactly 15.0, though the number itself is stored exactly, so the first said 16 digits; it has 15. All four languages printed the same. The loop does at most 19 divisions for any 64-bit number and is always right: use it, or the length of the number written as a string.
Building a number back up
result × 10 + digit appends a digit: 48 × 10 + 3 = 483. Feeding it the digits in the order they come off reverses the number. Palindrome Number needs only half of that: reverse the back half and compare it with the front, which never overflows and never builds a string:
#include <bits/stdc++.h>
using namespace std;
bool isPalindrome(int x) { // without a string, reversing only the back half
if (x < 0 || (x % 10 == 0 && x != 0))
return false; // a minus sign, or a last 0 the first digit can't match
int half = 0;
while (x > half) {
half = half * 10 + x % 10;
x /= 10;
}
return x == half || x == half / 10; // an odd count leaves the middle digit in half
}
int main() {
for (int x : {121, -121, 10, 1221, 12321, 0})
printf("%d %s\n", x, isPalindrome(x) ? "yes" : "no");
}
// 121 yes
// -121 no
// 10 no
// 1221 yes
// 12321 yes
// 0 yesdef is_palindrome(x): # without a string, reversing only the back half
if x < 0 or (x % 10 == 0 and x != 0):
return False # a minus sign, or a last 0 the first digit can't match
half = 0
while x > half:
half = half * 10 + x % 10
x //= 10
return x == half or x == half // 10 # an odd count leaves the middle digit in half
for x in (121, -121, 10, 1221, 12321, 0):
print(x, "yes" if is_palindrome(x) else "no")
# 121 yes
# -121 no
# 10 no
# 1221 yes
# 12321 yes
# 0 yespublic class Main {
static boolean isPalindrome(int x) { // without a string, reversing only the back half
if (x < 0 || (x % 10 == 0 && x != 0))
return false; // a minus sign, or a last 0 the first digit can't match
int half = 0;
while (x > half) {
half = half * 10 + x % 10;
x /= 10;
}
return x == half || x == half / 10; // an odd count leaves the middle digit in half
}
public static void main(String[] args) {
for (int x : new int[]{121, -121, 10, 1221, 12321, 0})
System.out.println(x + " " + (isPalindrome(x) ? "yes" : "no"));
}
}
// 121 yes
// -121 no
// 10 no
// 1221 yes
// 12321 yes
// 0 yesfunction isPalindrome(x) { // without a string, reversing only the back half
if (x < 0 || (x % 10 === 0 && x !== 0))
return false; // a minus sign, or a last 0 the first digit can't match
let half = 0;
while (x > half) {
half = half * 10 + x % 10;
x = Math.floor(x / 10);
}
return x === half || x === Math.floor(half / 10); // an odd count leaves the middle digit in half
}
for (const x of [121, -121, 10, 1221, 12321, 0])
console.log(x, isPalindrome(x) ? "yes" : "no");
// 121 yes
// -121 no
// 10 no
// 1221 yes
// 12321 yes
// 0 yesThe loop stops once the reversed half catches up with what's left: 1221 leaves 12 and 12. With an odd count the middle digit ends up on the reversed side (12321 leaves 12 and 123), so it's dropped with / 10 before comparing. A negative number can't be a palindrome, since the minus sign has no partner, and neither can a number ending in 0 other than 0 itself, so both return early.
Plus One gives a number as an array of digits and adds 1 the way you do on paper: from the right, a digit below 9 goes up by one and that's the end; a 9 becomes 0 and the 1 carries left; if every digit was 9, a new 1 goes in front:
#include <bits/stdc++.h>
using namespace std;
vector<int> plusOne(vector<int> digits) { // the digits of a number, most significant first
for (int i = digits.size() - 1; i >= 0; i--) {
if (digits[i] < 9) {
digits[i]++; // no carry: done
return digits;
}
digits[i] = 0; // 9 + 1: write 0, carry 1 left
}
digits.insert(digits.begin(), 1); // every digit was 9
return digits;
}
int main() {
for (vector<int> d : vector<vector<int>>{{1, 2, 3}, {4, 3, 9, 9}, {9, 9, 9}}) {
for (int x : d) printf("%d", x);
printf(" -> ");
for (int x : plusOne(d)) printf("%d", x);
printf("\n");
}
}
// 123 -> 124
// 4399 -> 4400
// 999 -> 1000def plus_one(digits): # the digits of a number, most significant first
digits = digits[:] # keep the caller's list
for i in range(len(digits) - 1, -1, -1):
if digits[i] < 9:
digits[i] += 1 # no carry: done
return digits
digits[i] = 0 # 9 + 1: write 0, carry 1 left
return [1] + digits # every digit was 9
for d in ([1, 2, 3], [4, 3, 9, 9], [9, 9, 9]):
print("".join(map(str, d)), "->", "".join(map(str, plus_one(d))))
# 123 -> 124
# 4399 -> 4400
# 999 -> 1000import java.util.*;
public class Main {
static int[] plusOne(int[] digits) { // the digits of a number, most significant first
digits = digits.clone(); // keep the caller's array
for (int i = digits.length - 1; i >= 0; i--) {
if (digits[i] < 9) {
digits[i]++; // no carry: done
return digits;
}
digits[i] = 0; // 9 + 1: write 0, carry 1 left
}
int[] longer = new int[digits.length + 1];
longer[0] = 1; // every digit was 9: 1 followed by zeros
return longer;
}
static String joined(int[] digits) {
StringBuilder s = new StringBuilder();
for (int x : digits) s.append(x);
return s.toString();
}
public static void main(String[] args) {
for (int[] d : new int[][]{{1, 2, 3}, {4, 3, 9, 9}, {9, 9, 9}})
System.out.println(joined(d) + " -> " + joined(plusOne(d)));
}
}
// 123 -> 124
// 4399 -> 4400
// 999 -> 1000function plusOne(digits) { // the digits of a number, most significant first
digits = digits.slice(); // keep the caller's array
for (let i = digits.length - 1; i >= 0; i--) {
if (digits[i] < 9) {
digits[i]++; // no carry: done
return digits;
}
digits[i] = 0; // 9 + 1: write 0, carry 1 left
}
return [1, ...digits]; // every digit was 9
}
for (const d of [[1, 2, 3], [4, 3, 9, 9], [9, 9, 9]])
console.log(d.join(""), "->", plusOne(d).join(""));
// 123 -> 124
// 4399 -> 4400
// 999 -> 1000It's O(n) in the number of digits, and it never turns the array into a number. The array can hold 100 digits, and no integer type holds that; Mistakes that cost an interview shows what happens when you try.
Reversing without overflow
A 32-bit int holds −2,147,483,648 to 2,147,483,647. The reverse has to be checked before result × 10 + digit runs: in C++ and Java the overflow has already happened by the time you could look at the result. Two comparisons are enough:
- if result is above 214,748,364 (that's 2,147,483,647 / 10), result × 10 alone passes the limit;
- if it equals 214,748,364, result × 10 is 2,147,483,640, and only a last digit above 7 pushes it over.
The negative side is the same with −214,748,364 and −8, because the smallest int ends in 8:

#include <bits/stdc++.h>
using namespace std;
int reverse(int x) { // 0 when the reverse doesn't fit in 32 bits
int result = 0;
while (x != 0) {
int digit = x % 10; // negative when x is: C++ keeps the dividend's sign
x /= 10;
if (result > INT_MAX / 10 || (result == INT_MAX / 10 && digit > 7)) return 0;
if (result < INT_MIN / 10 || (result == INT_MIN / 10 && digit < -8)) return 0;
result = result * 10 + digit; // safe: checked before it could pass the limit
}
return result;
}
int main() {
for (int x : {123, -123, 120, 1534236469, 1463847412, INT_MIN})
printf("%d -> %d\n", x, reverse(x));
}
// 123 -> 321
// -123 -> -321
// 120 -> 21
// 1534236469 -> 0
// 1463847412 -> 2147483641
// -2147483648 -> 0INT_MAX = 2 ** 31 - 1 # 2,147,483,647; the smallest int is -2,147,483,648
def reverse(x): # 0 when the reverse doesn't fit in 32 bits
sign = -1 if x < 0 else 1
x = abs(x) # Python's % would make the digits of a negative number positive
last = 7 if sign == 1 else 8 # the largest last digit that still fits
result = 0
while x != 0:
digit = x % 10
x //= 10
if result > INT_MAX // 10 or (result == INT_MAX // 10 and digit > last):
return 0 # checked before result * 10 could pass the limit
result = result * 10 + digit
return sign * result
for x in (123, -123, 120, 1534236469, 1463847412, -2147483648):
print(x, "->", reverse(x))
# 123 -> 321
# -123 -> -321
# 120 -> 21
# 1534236469 -> 0
# 1463847412 -> 2147483641
# -2147483648 -> 0public class Main {
static int reverse(int x) { // 0 when the reverse doesn't fit in 32 bits
int result = 0;
while (x != 0) {
int digit = x % 10; // negative when x is: Java keeps the dividend's sign
x /= 10;
if (result > Integer.MAX_VALUE / 10 || (result == Integer.MAX_VALUE / 10 && digit > 7)) return 0;
if (result < Integer.MIN_VALUE / 10 || (result == Integer.MIN_VALUE / 10 && digit < -8)) return 0;
result = result * 10 + digit; // safe: checked before it could pass the limit
}
return result;
}
public static void main(String[] args) {
for (int x : new int[]{123, -123, 120, 1534236469, 1463847412, Integer.MIN_VALUE})
System.out.println(x + " -> " + reverse(x));
}
}
// 123 -> 321
// -123 -> -321
// 120 -> 21
// 1534236469 -> 0
// 1463847412 -> 2147483641
// -2147483648 -> 0const INT_MAX = 2 ** 31 - 1, INT_MIN = -(2 ** 31);
function reverse(x) { // 0 when the reverse doesn't fit in 32 bits
let result = 0;
while (x !== 0) {
const digit = x % 10; // negative when x is, as in C++ and Java
x = Math.trunc(x / 10);
if (result > Math.trunc(INT_MAX / 10) || (result === Math.trunc(INT_MAX / 10) && digit > 7)) return 0;
if (result < Math.trunc(INT_MIN / 10) || (result === Math.trunc(INT_MIN / 10) && digit < -8)) return 0;
result = result * 10 + digit; // checked before it could pass the limit
}
return result;
}
for (const x of [123, -123, 120, 1534236469, 1463847412, INT_MIN])
console.log(x, "->", reverse(x));
// 123 -> 321
// -123 -> -321
// 120 -> 21
// 1534236469 -> 0
// 1463847412 -> 2147483641
// -2147483648 -> 01,534,236,469 now gives 0, 120 gives 21 (the trailing 0 becomes a leading 0 and disappears), and 1,463,847,412 gives 2,147,483,641, which fits. −2,147,483,648 gives 0: its reverse, −8,463,847,412, is far past the smallest int.
In C++, Java and JavaScript the digit of a negative number is negative, so one loop handles both signs with the 7 and the −8. Python's % makes those digits positive, so its version takes the sign off first and lets the last digit be at most 7 or 8. JavaScript could hold the true reverse (its numbers are exact up to 2⁵³) and check at the end instead, but the version above keeps the check the problem means.
Other bases
Nothing in the loop is special to 10. In base b, n % b is the last digit and n / b drops it; reading digits back multiplies by b and adds, left to right (Horner's rule):
#include <bits/stdc++.h>
using namespace std;
const string DIGITS = "0123456789ABCDEF";
string toBase(long long n, int base) { // n >= 0 written in base 2 to 16
string out;
do {
out = DIGITS[n % base] + out; // the last digit goes in front
n /= base;
} while (n != 0);
return out;
}
long long fromBase(const string& s, int base) { // Horner's rule: one place left, then add the digit
long long value = 0;
for (char ch : s) value = value * base + (long long) DIGITS.find(ch);
return value;
}
int main() {
for (pair<long long, int> c : vector<pair<long long, int>>{{13, 2}, {255, 16}, {4837, 8}}) {
string s = toBase(c.first, c.second);
printf("%lld in base %d is %s, and back: %lld\n", c.first, c.second, s.c_str(), fromBase(s, c.second));
}
}
// 13 in base 2 is 1101, and back: 13
// 255 in base 16 is FF, and back: 255
// 4837 in base 8 is 11345, and back: 4837DIGITS = "0123456789ABCDEF"
def to_base(n, base): # n >= 0 written in base 2 to 16
out = ""
while True:
out = DIGITS[n % base] + out # the last digit goes in front
n //= base
if n == 0:
return out
def from_base(s, base): # Horner's rule: one place left, then add the digit
value = 0
for ch in s:
value = value * base + DIGITS.index(ch)
return value
for n, base in ((13, 2), (255, 16), (4837, 8)):
s = to_base(n, base)
print(f"{n} in base {base} is {s}, and back: {from_base(s, base)}")
# 13 in base 2 is 1101, and back: 13
# 255 in base 16 is FF, and back: 255
# 4837 in base 8 is 11345, and back: 4837public class Main {
static final String DIGITS = "0123456789ABCDEF";
static String toBase(long n, int base) { // n >= 0 written in base 2 to 16
StringBuilder out = new StringBuilder();
do {
out.insert(0, DIGITS.charAt((int) (n % base))); // the last digit goes in front
n /= base;
} while (n != 0);
return out.toString();
}
static long fromBase(String s, int base) { // Horner's rule: one place left, then add the digit
long value = 0;
for (char ch : s.toCharArray()) value = value * base + DIGITS.indexOf(ch);
return value;
}
public static void main(String[] args) {
long[][] cases = {{13, 2}, {255, 16}, {4837, 8}};
for (long[] c : cases) {
String s = toBase(c[0], (int) c[1]);
System.out.println(c[0] + " in base " + c[1] + " is " + s + ", and back: " + fromBase(s, (int) c[1]));
}
}
}
// 13 in base 2 is 1101, and back: 13
// 255 in base 16 is FF, and back: 255
// 4837 in base 8 is 11345, and back: 4837const DIGITS = "0123456789ABCDEF";
function toBase(n, base) { // n >= 0 written in base 2 to 16
let out = "";
do {
out = DIGITS[n % base] + out; // the last digit goes in front
n = Math.floor(n / base);
} while (n !== 0);
return out;
}
function fromBase(s, base) { // Horner's rule: one place left, then add the digit
let value = 0;
for (const ch of s) value = value * base + DIGITS.indexOf(ch);
return value;
}
for (const [n, base] of [[13, 2], [255, 16], [4837, 8]]) {
const s = toBase(n, base);
console.log(`${n} in base ${base} is ${s}, and back: ${fromBase(s, base)}`);
}
// 13 in base 2 is 1101, and back: 13
// 255 in base 16 is FF, and back: 255
// 4837 in base 8 is 11345, and back: 483713 is 1101 in binary: 8 + 4 + 0 + 1. A number has ⌊log_b n⌋ + 1 digits in base b, so a bigger base means fewer digits: 4,837 takes 4 in base 10, 5 in base 8 and 13 in binary.
Spreadsheet columns look like base 26 but aren't quite. They run A to Z, then AA, AB and so on, with no letter for zero: the digits go from 1 to 26 instead of 0 to 25. Plain base 26 breaks exactly where a remainder is 0. Run as plain base 26 with @ (the character before A) for the 0, it printed A@ for 26, B@ for 52 and AA@ for 702. The fix is to take 1 off before each digit:
#include <bits/stdc++.h>
using namespace std;
string columnTitle(int n) { // 1 -> A, 26 -> Z, 27 -> AA: base 26 with digits 1 to 26, no zero
string title;
while (n > 0) {
n--; // shift 1..26 down to 0..25 before taking the digit
title = char('A' + n % 26) + title;
n /= 26;
}
return title;
}
int main() {
for (int n : {1, 26, 27, 52, 701, 702, 703})
printf("%d %s\n", n, columnTitle(n).c_str());
}
// 1 A
// 26 Z
// 27 AA
// 52 AZ
// 701 ZY
// 702 ZZ
// 703 AAAdef column_title(n): # 1 -> A, 26 -> Z, 27 -> AA: base 26 with digits 1 to 26, no zero
title = ""
while n > 0:
n -= 1 # shift 1..26 down to 0..25 before taking the digit
title = chr(ord("A") + n % 26) + title
n //= 26
return title
for n in (1, 26, 27, 52, 701, 702, 703):
print(n, column_title(n))
# 1 A
# 26 Z
# 27 AA
# 52 AZ
# 701 ZY
# 702 ZZ
# 703 AAApublic class Main {
static String columnTitle(int n) { // 1 -> A, 26 -> Z, 27 -> AA: base 26 with digits 1 to 26, no zero
StringBuilder title = new StringBuilder();
while (n > 0) {
n--; // shift 1..26 down to 0..25 before taking the digit
title.insert(0, (char) ('A' + n % 26));
n /= 26;
}
return title.toString();
}
public static void main(String[] args) {
for (int n : new int[]{1, 26, 27, 52, 701, 702, 703})
System.out.println(n + " " + columnTitle(n));
}
}
// 1 A
// 26 Z
// 27 AA
// 52 AZ
// 701 ZY
// 702 ZZ
// 703 AAAfunction columnTitle(n) { // 1 -> A, 26 -> Z, 27 -> AA: base 26 with digits 1 to 26, no zero
let title = "";
while (n > 0) {
n--; // shift 1..26 down to 0..25 before taking the digit
title = String.fromCharCode(65 + n % 26) + title;
n = Math.floor(n / 26);
}
return title;
}
for (const n of [1, 26, 27, 52, 701, 702, 703])
console.log(n, columnTitle(n));
// 1 A
// 26 Z
// 27 AA
// 52 AZ
// 701 ZY
// 702 ZZ
// 703 AAA
26 − 1 = 25 is Z with nothing left over. 52 − 1 = 51 is 1 × 26 + 25: Z, then 1 − 1 = 0 is A, so AZ. 702 is ZZ and 703 is AAA.
Numbers longer than any integer
When a number has 200 digits it comes as a string, and the arithmetic is the paper method, digit by digit. Multiply Strings asks for the product of two digit strings without converting them.
Count the digits from the left from 0. Digit i of an m-digit num1 is worth 10 to the power m − 1 − i, and digit j of an n-digit num2 is worth 10 to the n − 1 − j, so their product lands m + n − 2 − i − j places from the right. In a result of m + n cells, that's cell i + j + 1, and whatever passes 9 carries into cell i + j. A product of an m-digit and an n-digit number never has more than m + n digits, so m + n cells are always enough:

#include <bits/stdc++.h>
using namespace std;
string multiply(const string& num1, const string& num2) { // two numbers as digit strings
int m = num1.size(), n = num2.size();
vector<int> cells(m + n, 0); // the product has at most m + n digits
for (int i = m - 1; i >= 0; i--)
for (int j = n - 1; j >= 0; j--) {
int product = (num1[i] - '0') * (num2[j] - '0');
int total = product + cells[i + j + 1]; // digit i times digit j lands at i + j + 1
cells[i + j + 1] = total % 10;
cells[i + j] += total / 10; // the carry moves one place left
}
string text;
for (int c : cells)
if (!(text.empty() && c == 0)) text += char('0' + c); // skip the leading zeros
return text.empty() ? "0" : text; // every cell 0: the product is 0
}
int main() {
printf("%s %s %s\n", multiply("123", "456").c_str(), multiply("0", "52").c_str(),
multiply("999", "999").c_str());
}
// 56088 0 998001def multiply(num1, num2): # two numbers as digit strings
m, n = len(num1), len(num2)
cells = [0] * (m + n) # the product has at most m + n digits
for i in range(m - 1, -1, -1):
for j in range(n - 1, -1, -1):
product = (ord(num1[i]) - ord("0")) * (ord(num2[j]) - ord("0"))
total = product + cells[i + j + 1] # digit i times digit j lands at i + j + 1
cells[i + j + 1] = total % 10
cells[i + j] += total // 10 # the carry moves one place left
text = "".join(map(str, cells)).lstrip("0") # skip the leading zeros
return text or "0" # every cell 0: the product is 0
print(multiply("123", "456"), multiply("0", "52"), multiply("999", "999"))
# 56088 0 998001public class Main {
static String multiply(String num1, String num2) { // two numbers as digit strings
int m = num1.length(), n = num2.length();
int[] cells = new int[m + n]; // the product has at most m + n digits
for (int i = m - 1; i >= 0; i--)
for (int j = n - 1; j >= 0; j--) {
int product = (num1.charAt(i) - '0') * (num2.charAt(j) - '0');
int total = product + cells[i + j + 1]; // digit i times digit j lands at i + j + 1
cells[i + j + 1] = total % 10;
cells[i + j] += total / 10; // the carry moves one place left
}
StringBuilder text = new StringBuilder();
for (int c : cells)
if (!(text.length() == 0 && c == 0)) text.append(c); // skip the leading zeros
return text.length() == 0 ? "0" : text.toString(); // every cell 0: the product is 0
}
public static void main(String[] args) {
System.out.println(multiply("123", "456") + " " + multiply("0", "52") + " "
+ multiply("999", "999"));
}
}
// 56088 0 998001function multiply(num1, num2) { // two numbers as digit strings
const m = num1.length, n = num2.length;
const cells = new Array(m + n).fill(0); // the product has at most m + n digits
for (let i = m - 1; i >= 0; i--)
for (let j = n - 1; j >= 0; j--) {
const product = (num1.charCodeAt(i) - 48) * (num2.charCodeAt(j) - 48);
const total = product + cells[i + j + 1]; // digit i times digit j lands at i + j + 1
cells[i + j + 1] = total % 10;
cells[i + j] += Math.floor(total / 10); // the carry moves one place left
}
const text = cells.join("").replace(/^0+/, ""); // skip the leading zeros
return text || "0"; // every cell 0: the product is 0
}
console.log(multiply("123", "456"), multiply("0", "52"), multiply("999", "999"));
// 56088 0 998001123 × 456 came out 56,088 in all four languages, 999 × 999 = 998,001, and two 200-digit numbers multiplied this way matched Python's own integers digit for digit. Every pair of digits is multiplied once: O(m · n) time and O(m + n) cells.
The leading zeros matter twice: 123 × 456 fills only 5 of its 6 cells, and 0 × 52 fills none of its 3, which must come out as 0, not as an empty string or 000.
Carry the One. Multiply two numbers the way the code does. For each pair of digits, pick the cell its product lands in and watch the carry move left; at the end, decide which leading zeros go.

Spotting it
The clues: "the digits of n", "reverse", "without converting it to a string", "may not fit in a 32-bit integer", numbers given as strings, a column title or a base. Near misses:
- Roman to Integer reads symbols, not place values: one lookup per symbol and one comparison with the next (Strings, Parsing in One Pass).
- String to Integer (atoi) builds a number from characters with the same overflow check before the multiply, but most of its work is signs, spaces and stray characters (Strings, Parsing in One Pass).
- Happy Number repeats "add up the squares of the digits", but the question is whether that sequence loops, which is cycle detection (Linked List, Fast and Slow Pointers).
Mistakes that cost an interview
Checking for overflow after it happened. The first loop above printed 1,056,389,759 in C++ and Java for 1,534,236,469. Compare against 214,748,364 before the multiply.
while n > 0 for the digits of 0. It counts 0 digits. Divide first, test after.
Python's % on a negative number. −123 % 10 is 7 in Python and −3 in C++, Java and JavaScript. Take the sign off first, or know which one your language does.
Counting digits with logarithms. ⌊log₁₀ n⌋ + 1 said 16 for 999,999,999,999,999 and log(n) / log(10) said 3 for 1,000, in all four languages. Count with the loop.
Plus One by turning the digits into a number. On these 19 digits:
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<int> digits = {9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 7};
long long number = 0;
for (int d : digits) number = number * 10 + d; // the largest long long, exactly
printf("%lld\n", number + 1); // plus one: past the largest long long
}
// -9223372036854775808digits = [9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 7]
number = int("".join(map(str, digits))) # right only because Python's ints have no size limit
print(number + 1)
# 9223372036854775808public class Main {
public static void main(String[] args) {
int[] digits = {9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 7};
long number = 0;
for (int d : digits) number = number * 10 + d; // Long.MAX_VALUE, exactly
System.out.println(number + 1); // plus one: it wraps around
}
}
// -9223372036854775808const digits = [9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 7];
const number = Number(digits.join("")); // a double: about 16 significant digits
console.log(number + 1);
// 9223372036854776000C++ and Java printed −9,223,372,036,854,775,808: 9,223,372,036,854,775,807 is the largest 64-bit integer, and one more wraps it to the smallest (in C++, undefined behaviour again). JavaScript printed 9,223,372,036,854,776,000: a double keeps about 16 significant digits. Python was right only because its ints have no size limit, and the array can be 100 digits long. Carry through the array instead.
Spreadsheet columns as plain base 26. 26 came out A@. Take 1 off before each digit.
Leaving the leading zeros in Multiply Strings. Without the strip, 0 × 52 returned '000'; with a strip and nothing to fall back on, it returned '', an empty string. Strip, and return "0" when nothing is left.
Check yourself
Quiz
1 / 6Multiple choice
int count = 0;
while (n > 0) {
n /= 10;
count++;
}Multiple choice
Multiple choice
result = result * 10 + digit?Multiple choice
Multiple choice
Multiple choice
long long n = 999999999999999;
printf("%d\n", (int) floor(log10((double) n)) + 1);Practice
In this section, easy to hard:
- Count Digits of a Number: the digit loop, and 0 has one digit.
- Palindrome Number: reverse the back half and compare.
- Armstrong Number: the loop twice, digits then powers.
- Plus One: a carry through an array.
- Excel Sheet Column Title: base 26 with no zero digit.
- Reverse Integer: the overflow check before the multiply.
- Multiply Strings: digit products at i + j + 1, then the leading zeros.
Next: Divisors, GCD and Primes, where the loop runs over a number's divisors instead of its digits.