Digit Reversal with Overflow Guard
You're in the first round of a Google interview. The interviewer types reverse(1234) on a shared screen and asks, "Write a function that returns 4321. Simple enough, right?" You nod, start coding, and then they add: "Oh, and what happens when the reversed number is too large for a 32-bit integer?" That follow-up is the whole problem. Reversing digits is straightforward arithmetic. Detecting overflow without using a larger data type is where this problem separates candidates who understand integer boundaries from those who don't. On Firecode, this is "Digit Reversal with Overflow Guard," but you'll see it across the industry as Reverse Integer.
TL;DR
Extract digits from right to left using x % 10, build the reversed number with reversed = reversed * 10 + pop, and divide x by 10 each iteration. Before every multiplication, compare reversed against INT_MAX / 10 (or INT_MIN / 10 for negatives) to catch overflow. If it would overflow, return 0. This runs in O(log n) time and O(1) space.
Why This Problem Matters
Reverse Integer is one of the most frequently asked interview problems, appearing at Google, Amazon, Microsoft, Yahoo, and TCS. It tests two distinct skills: basic modular arithmetic (extracting and rearranging digits) and defensive programming against integer overflow. The overflow guard is what makes this a medium-difficulty problem instead of an easy one, and it's the part that trips up most candidates.
Understanding the Problem
Given a signed 32-bit integer x, reverse its digits and return the result. If the reversed integer falls outside the range [-2^31, 2^31 - 1], return 0.
Examples:
reverse(1234)returns4321reverse(-1234)returns-4321reverse(140)returns41reverse(2147483647)returns0(reversed value 7463847412 overflows)
Constraints:
- The integer
xsatisfies-2^31 <= x <= 2^31 - 1 - You cannot use 64-bit integers
Edge Cases to Watch
- Negative numbers: The sign should be preserved
- Trailing zeros:
140becomes41, not041 - INT_MAX / INT_MIN: These overflow when reversed
- Zero: Returns 0
- Single digit: Returns itself
The Core Idea: Modular Arithmetic
Reversing an integer digit by digit boils down to two operations you probably learned in grade school:
x % 10gives you the last digit (the "ones" place)x / 10(integer division) removes the last digit
By repeatedly extracting the last digit and appending it to a running total, you build the reversed number. The formula at each step is:
pop = x % 10
x = x / 10
reversed = reversed * 10 + pop
For x = 1234:
Loading visualization...
Each step peels off the rightmost digit and shifts it into position in the reversed number. When x reaches 0, you're done.
Negative Numbers Work Automatically
In Java, C++, Go, and most compiled languages, the % operator preserves the sign of the dividend. This means -1234 % 10 evaluates to -4, not 6. The extracted digit carries its sign naturally, so the same loop handles both positive and negative inputs with no branching.
Loading visualization...
Trailing Zeros Disappear Naturally
When x = 140, the first extracted digit is 0. Multiplying reversed (which is 0) by 10 and adding 0 gives 0, so the leading zero in the reversed number simply vanishes:
Loading visualization...
The Overflow Guard
The digit extraction loop is the easy part. The real challenge is preventing reversed * 10 + pop from exceeding the 32-bit signed integer range. You cannot detect overflow after it happens, because in C++ the behavior is undefined, and in Java the value silently wraps around. You need to check before performing the multiplication.
32-Bit Integer Boundaries
Loading visualization...
INT_MAX is 2,147,483,647 and INT_MIN is -2,147,483,648. The key values for our guard are INT_MAX / 10 = 214,748,364 and INT_MIN / 10 = -214,748,364.
Two Conditions Per Side
The overflow check has two cases for positive numbers and two for negative:
Positive overflow (before computing reversed * 10 + pop):
- If
reversed > INT_MAX / 10, thenreversed * 10already exceedsINT_MAX. Return 0. - If
reversed == INT_MAX / 10andpop > 7, thenreversed * 10 + pop > 2,147,483,647. Return 0.
Negative underflow:
- If
reversed < INT_MIN / 10, thenreversed * 10already goes belowINT_MIN. Return 0. - If
reversed == INT_MIN / 10andpop < -8, thenreversed * 10 + pop < -2,147,483,648. Return 0.
Loading visualization...
The numbers 7 and -8 come directly from the last digits of INT_MAX and INT_MIN.
Overflow in Action
When x = 2147483647, the reversed value would be 7463847412, which blows past INT_MAX. The guard catches this partway through the loop:
Loading visualization...
Implementation
Prefer a different language? Jump to solutions in other languages.
Here is the complete Java solution:
public class Solution {
public int reverse(int x) {
int reversed = 0;
while (x != 0) {
int pop = x % 10;
x /= 10;
// Overflow guard: positive side
if (reversed > Integer.MAX_VALUE / 10
|| (reversed == Integer.MAX_VALUE / 10 && pop > 7)) {
return 0;
}
// Overflow guard: negative side
if (reversed < Integer.MIN_VALUE / 10
|| (reversed == Integer.MIN_VALUE / 10 && pop < -8)) {
return 0;
}
reversed = reversed * 10 + pop;
}
return reversed;
}
}
Step-by-Step Walkthrough
For reverse(1234):
| Iteration | x | pop (x % 10) | x /= 10 | Overflow check | reversed * 10 + pop |
|---|---|---|---|---|---|
| 1 | 1234 | 4 | 123 | 0 < 214748364, safe | 4 |
| 2 | 123 | 3 | 12 | 4 < 214748364, safe | 43 |
| 3 | 12 | 2 | 1 | 43 < 214748364, safe | 432 |
| 4 | 1 | 1 | 0 | 432 < 214748364, safe | 4321 |
Loop ends when x == 0. Return 4321.
For reverse(-2147483648) (INT_MIN):
| Iteration | x | pop | Overflow check | Result |
|---|---|---|---|---|
| 1 | -2147483648 | -8 | reversed = 0, safe | -8 |
| 2 | -214748364 | -4 | -8 > -214748364, safe | -84 |
| ... | ... | ... | ... | ... |
| 9 | -2 | -2 | -846384741 < -214748364, overflow detected! | return 0 |
Complexity Analysis
Time Complexity: O(log n)
The number of digits in an integer n is \lfloor \log_{10}(|n|) \rfloor + 1. Each iteration processes one digit, so the loop runs at most 10 times for any 32-bit integer. This is technically O(log n), though in practice it is bounded by a small constant.
Space Complexity: O(1)
The algorithm uses three variables (reversed, pop, and the mutating x) regardless of input size. No strings, arrays, or additional data structures are allocated.
Common Pitfalls
-
Forgetting the overflow check entirely: Without the guard,
reversed * 10 + popcan silently wrap around in Java or trigger undefined behavior in C++. -
Checking overflow after the multiplication: At that point the damage is done. You must check before
reversed * 10. -
Using a 64-bit long: This works but violates the problem constraint. Interviewers will ask you to solve it within 32-bit arithmetic.
-
Mishandling negative modulus: In Python and Ruby,
%and//round toward negative infinity rather than toward zero. If you're working in those languages, you need to handle the sign separately. See the Python and Ruby solutions below. -
Off-by-one on boundary digits: The threshold digits are 7 (from
INT_MAX's last digit) and -8 (fromINT_MIN's last digit). Using the wrong constants causes subtle bugs on boundary inputs.
Interview Tips
When solving this in an interview:
-
Start with the simple case. Write the extraction loop first (
pop = x % 10,x /= 10,reversed = reversed * 10 + pop). Show that it works for1234before adding overflow logic. -
Bring up overflow proactively. Don't wait for the interviewer to ask "what about large numbers?" Mention the 32-bit constraint yourself and explain why you need a pre-check.
-
Explain the guard in terms of INT_MAX / 10. The core insight is that if
reversedis already larger thanINT_MAX / 10, thenreversed * 10will definitely overflow. The boundary case (reversed == INT_MAX / 10) requires checking the digit being appended. -
Test with specific inputs:
0,-1,10,INT_MAX,INT_MIN. Walk through at least one overflow case to demonstrate the guard in action. -
Mention the language caveat. If the interviewer asks about Python or Ruby, point out that their modulus operator behaves differently for negative numbers, and show how your approach adapts.
Key Takeaways
- Reverse digits with repeated
x % 10(extract) andx / 10(shrink), building the result asreversed = reversed * 10 + pop. - The overflow guard compares
reversedagainstINT_MAX / 10before multiplying. Ifreversedexceeds this threshold, or equals it with a digit larger than 7, return 0. Mirror the check forINT_MIN / 10and -8 on the negative side. - In Java, C++, Go, and Rust, the
%operator preserves sign, so the same loop handles positive and negative inputs. Python and Ruby require sign-separated handling because their modulus rounds toward negative infinity. - Time is O(log n) with at most 10 iterations for 32-bit integers. Space is O(1) with no auxiliary data structures.
- Always validate with INT_MAX and INT_MIN as inputs. These are the cases interviewers use to verify your overflow guard is correct.
Solutions in Other Languages
Python
Python integers have arbitrary precision, so they never overflow natively. However, the problem asks us to simulate 32-bit overflow behavior. Since Python's % and // operators round toward negative infinity (not toward zero), we handle the sign separately to keep the logic clean.
class Solution:
def reverse(self, x: int) -> int:
INT_MAX = 2**31 - 1
INT_MIN = -2**31
sign = -1 if x < 0 else 1
x *= sign
reversed_x = 0
while x != 0:
pop = x % 10
x //= 10
if reversed_x > (INT_MAX - pop) // 10:
return 0
reversed_x = reversed_x * 10 + pop
return sign * reversed_x
JavaScript
JavaScript does not have a native 32-bit integer type, so Math.pow(2, 31) - 1 and Math.floor / Math.ceil are used for the bounds. The bitwise OR trick (x / 10) | 0 truncates toward zero, matching C-style integer division.
class Solution {
reverse(x) {
let reversed = 0;
const INT_MAX = Math.pow(2, 31) - 1;
const INT_MIN = -Math.pow(2, 31);
while (x !== 0) {
const pop = x % 10;
x = (x / 10) | 0;
if (reversed > Math.floor(INT_MAX / 10)
|| (reversed === Math.floor(INT_MAX / 10) && pop > 7)) {
return 0;
}
if (reversed < Math.ceil(INT_MIN / 10)
|| (reversed === Math.ceil(INT_MIN / 10) && pop < -8)) {
return 0;
}
reversed = reversed * 10 + pop;
}
return reversed;
}
}
TypeScript
Identical to the JavaScript solution with type annotations added.
class Solution {
reverse(x: number): number {
let reversed = 0;
const INT_MAX = Math.pow(2, 31) - 1;
const INT_MIN = -Math.pow(2, 31);
while (x !== 0) {
const pop = x % 10;
x = Math.trunc(x / 10);
if (reversed > Math.floor(INT_MAX / 10)
|| (reversed === Math.floor(INT_MAX / 10) && pop > 7)) {
return 0;
}
if (reversed < Math.ceil(INT_MIN / 10)
|| (reversed === Math.ceil(INT_MIN / 10) && pop < -8)) {
return 0;
}
reversed = reversed * 10 + pop;
}
return reversed;
}
}
C++
C++ integer overflow on signed types is undefined behavior, which makes the pre-check essential. The <limits> header provides std::numeric_limits<int>::max() and min().
#include <limits>
class Solution {
public:
int reverse(int x) {
int reversed = 0;
while (x != 0) {
int pop = x % 10;
x /= 10;
if (reversed > std::numeric_limits<int>::max() / 10
|| (reversed == std::numeric_limits<int>::max() / 10 && pop > 7)) {
return 0;
}
if (reversed < std::numeric_limits<int>::min() / 10
|| (reversed == std::numeric_limits<int>::min() / 10 && pop < -8)) {
return 0;
}
reversed = reversed * 10 + pop;
}
return reversed;
}
};
Go
Go does not have exceptions for integer overflow on fixed-width types; it wraps silently. The constants are defined as shifts: 1<<31 - 1 for INT_MAX and -1 << 31 for INT_MIN.
package solution
func (s *Solution) Reverse(x int) int {
const (
intMax = 1<<31 - 1
intMin = -1 << 31
)
result := 0
for x != 0 {
pop := x % 10
x /= 10
if result > intMax/10 || (result == intMax/10 && pop > 7) {
return 0
}
if result < intMin/10 || (result == intMin/10 && pop < -8) {
return 0
}
result = result*10 + pop
}
return result
}
Kotlin
Kotlin's Int is a 32-bit signed type. Int.MAX_VALUE and Int.MIN_VALUE provide the bounds directly.
class Solution {
fun reverse(x: Int): Int {
var reversed = 0
var num = x
while (num != 0) {
val pop = num % 10
num /= 10
if (reversed > Int.MAX_VALUE / 10
|| (reversed == Int.MAX_VALUE / 10 && pop > 7)) return 0
if (reversed < Int.MIN_VALUE / 10
|| (reversed == Int.MIN_VALUE / 10 && pop < -8)) return 0
reversed = reversed * 10 + pop
}
return reversed
}
}
Scala
Scala's Int behaves identically to Java's. Int.MaxValue and Int.MinValue provide the bounds.
class Solution {
def reverse(x: Int): Int = {
var num = x
var reversed = 0
while (num != 0) {
val pop = num % 10
num /= 10
if (reversed > Int.MaxValue / 10
|| (reversed == Int.MaxValue / 10 && pop > 7)) return 0
if (reversed < Int.MinValue / 10
|| (reversed == Int.MinValue / 10 && pop < -8)) return 0
reversed = reversed * 10 + pop
}
reversed
}
}
Rust
Rust's i32 panics on overflow in debug mode and wraps in release mode. The pre-check prevents either behavior from being reached.
impl Solution {
pub fn reverse(&self, mut x: i32) -> i32 {
let mut reversed = 0_i32;
while x != 0 {
let pop = x % 10;
x /= 10;
if reversed > i32::MAX / 10
|| (reversed == i32::MAX / 10 && pop > 7) {
return 0;
}
if reversed < i32::MIN / 10
|| (reversed == i32::MIN / 10 && pop < -8) {
return 0;
}
reversed = reversed * 10 + pop;
}
reversed
}
}
Swift
Swift's Int is 64-bit on modern platforms, but the problem constrains us to 32-bit bounds. We use Int32.max and Int32.min for the guard, converted to Int for arithmetic.
import Foundation
class Solution {
func reverse(_ x: Int) -> Int {
let maxDiv10 = Int(Int32.max) / 10
let minDiv10 = Int(Int32.min) / 10
var reversed = 0
var num = x
while num != 0 {
let pop = num % 10
num /= 10
if reversed > maxDiv10 || (reversed == maxDiv10 && pop > 7) { return 0 }
if reversed < minDiv10 || (reversed == minDiv10 && pop < -8) { return 0 }
reversed = reversed * 10 + pop
}
return reversed
}
}
C#
C#'s int is a 32-bit signed type. int.MaxValue and int.MinValue give the bounds.
public class Solution {
public int Reverse(int x) {
int reversed = 0;
while (x != 0) {
int pop = x % 10;
x /= 10;
if (reversed > int.MaxValue / 10
|| (reversed == int.MaxValue / 10 && pop > 7)) return 0;
if (reversed < int.MinValue / 10
|| (reversed == int.MinValue / 10 && pop < -8)) return 0;
reversed = reversed * 10 + pop;
}
return reversed;
}
}
Dart
Dart's int is 64-bit, but the problem constrains us to 32-bit bounds. The ~/ operator performs truncating integer division, and .remainder() preserves sign like C-style modulus.
class Solution {
int reverse(int x) {
int maxInt32 = 2147483647;
int minInt32 = -2147483648;
int reversed = 0;
while (x != 0) {
int pop = x.remainder(10);
x = x ~/ 10;
if (reversed > maxInt32 ~/ 10
|| (reversed == maxInt32 ~/ 10 && pop > 7)) return 0;
if (reversed < minInt32 ~/ 10
|| (reversed == minInt32 ~/ 10 && pop < -8)) return 0;
reversed = reversed * 10 + pop;
}
return reversed;
}
}
PHP
PHP integers are 64-bit on modern platforms, but we enforce 32-bit bounds manually. intdiv() performs truncating integer division.
class Solution {
public function reverse(int $x): int {
$INT_MAX = 2147483647;
$INT_MIN = -2147483648;
$reversed = 0;
while ($x !== 0) {
$pop = $x % 10;
$x = intdiv($x, 10);
if ($reversed > intdiv($INT_MAX, 10)
|| ($reversed === intdiv($INT_MAX, 10) && $pop > 7)) {
return 0;
}
if ($reversed < intdiv($INT_MIN, 10)
|| ($reversed === intdiv($INT_MIN, 10) && $pop < -8)) {
return 0;
}
$reversed = $reversed * 10 + $pop;
}
return $reversed;
}
}
Ruby
Like Python, Ruby's % and / round toward negative infinity. The solution handles negative numbers by adjusting the digit when Ruby's modulus returns a positive value for a negative dividend, and uses .truncate for toward-zero division.
class Solution
def reverse(x)
int_max = 2**31 - 1
int_min = -(2**31)
reversed = 0
while x != 0
digit = x % 10
digit -= 10 if x < 0 && digit > 0
x = (x / 10.0).truncate
return 0 if reversed > int_max / 10 || (reversed == int_max / 10 && digit > 7)
return 0 if reversed < int_min / 10 || (reversed == int_min / 10 && digit < -8)
reversed = reversed * 10 + digit
end
reversed
end
end
Practice Makes Perfect
Once you're comfortable with the overflow guard pattern, try these related problems to reinforce the underlying skills:
- Palindrome Number (determine if an integer reads the same forward and backward, using the same digit extraction loop)
- String to Integer (atoi) (parse a string into an integer with overflow checking)
- Add Two Numbers (sum digits with carry across linked list nodes)
Consistent practice is the key to succeeding in coding interviews. This problem and thousands of others are available on Firecode, where over 50,000 users have successfully prepared for technical interviews and landed six and seven-figure jobs at top tech companies. Whether you're just starting or sharpening up for a Google onsite, building fluency with integer math and boundary conditions will pay off.
Happy coding, and may your integers never overflow!