Ruby Integer Overflow in Coding Interviews: The Complete Guide

- Ruby integers have arbitrary precision — since 2.4,
FixnumandBignummerged into oneIntegerclass that grows without bound, so overflow is impossible. - Integer division floors toward negative infinity —
-7 / 2returns-4in Ruby, not-3like C, Java, or JavaScript; positive-only test cases will never surface this. %andremainderdiffer by sign —%gives the sign of the divisor (always non-negative when divisor is positive);remaindergives the sign of the dividend, matching C-style behavior.- Division by zero is inconsistent — integer
/0raisesZeroDivisionError, but42.0 / 0.0returnsInfinityand0.0 / 0.0returnsNaN. - Float precision caps at 2^53 — converting large integers to floats silently loses precision above this threshold; keep sentinels as integers or use
Float::INFINITY. to_iis lenient,Integer()is strict —"123abc".to_isilently returns123;Integer("123abc")raisesArgumentError.- Bitwise problems still need 32-bit masking — Ruby does not auto-truncate bit operations, so problems like Reverse Bits require an explicit
n &= 0xFFFFFFFF.
Ruby gives you a free pass on integer overflow. No INT_MAX. No silent wrap-around. No frantic reach for Long when your int runs out of room. In Ruby, integers just grow. Forever. Like debt.
That sounds like one less thing to worry about. It is, technically. But a lot of engineers hear "no overflow" and mentally file the entire "working with large numbers" problem as solved. It isn't. Not even close.
The division rules are different from every C-family language you've ever used, the modulo operator does unexpected things with negatives, float conversion silently eats precision above 2^53, and there are two completely different ways to parse a number string, only one of which will blow up in your face during an interview.
You know the overflow part. Here's everything else.
Ruby Integers Cannot Overflow
Since Ruby 2.4, all integers live in a single Integer class with arbitrary precision. Before that, the runtime juggled two internal classes: Fixnum for small values and Bignum for large ones. They merged in 2.4. If you see Fixnum in old code, it's just an alias. There is only Integer now.
2 ** 62 # => 4611686018427387904 (2 ** 62).class # => Integer 2 ** 100 # => 1267650600228229401496703205376 (2 ** 100).class # => Integer, not some special big-number type
The runtime handles memory automatically. A value will never silently wrap from MAX_INT to negative territory. If you write a Fibonacci function that computes fib(1000), you get the exact answer. All 209 digits of it.
Python does the same thing. Java and C++ do not. In those languages, overflow is a live concern every candidate needs to manage. In Ruby, you're safe. Completely, totally, dangerously safe.
Integer Division Floors Toward Negative Infinity
This is the one that gets you after your tests pass.
In C, Java, C++, and JavaScript, integer division truncates toward zero.
// In C or Java -7 / 2 // => -3 (truncates toward zero)
In Ruby, integer division floors toward negative infinity.
-7 / 2 # => -4 (floors down) 7 / 2 # => 3 (same as truncation when both positive) -7 / -2 # => 3 (same as truncation when both negative) 7 / -2 # => -4 (floors down)
For positive operands the results are identical, so simple test cases will never catch this. You write your solution. You test it on a few positive examples. You get the right answer. You feel great. Then the judge sends -7 and your answer is off by one and you have no idea why.
The divergence only appears when one operand goes negative. Your code runs, returns a plausible-looking number, and the judge marks it wrong. Silently. Confidently. Wrong.
If you need truncation toward zero, the C-style behavior, do this:
(-7.to_f / 2).truncate # => -3 (-7r / 2r).truncate # => -3
Every Ruby candidate who only tested positive inputs, eventually.
% and remainder Are Not the Same
Ruby gives you two ways to get the remainder of a division. They give different answers with negative numbers and the difference matters more than you'd expect.
% always returns a value with the sign of the divisor. This matches floored division.
17 % 5 # => 2 -17 % 5 # => 3 # positive: sign of divisor 17 % -5 # => -3 # negative: sign of divisor -17 % -5 # => -2
remainder always returns a value with the sign of the dividend. This is the C-style behavior.
17.remainder(5) # => 2 -17.remainder(5) # => -2 # negative: sign of dividend 17.remainder(-5) # => 2 -17.remainder(-5) # => -2
For hashing, cyclic index wrapping, or rotating through positions, use %. When the divisor is positive, the result is always non-negative, which is exactly what you need to safely index into an array. If you're porting code from Java or C++ where % can return negative values, use remainder to match the original behavior.
Division by Zero Is Not Uniform
This one trips up Ruby developers who come from statically typed languages, because the behavior depends entirely on whether you're working with integers or floats.
Integer division by integer zero raises ZeroDivisionError.
42 / 0 # ZeroDivisionError: divided by 0 42 % 0 # ZeroDivisionError: divided by 0
Float division by zero does not raise. It returns Infinity, -Infinity, or NaN, following IEEE 754 rules.
42.0 / 0.0 # => Infinity -42.0 / 0.0 # => -Infinity 0.0 / 0.0 # => NaN
The fdiv method on integers follows float rules even though you're calling it on an integer:
42.fdiv(0) # => Infinity 0.fdiv(0) # => NaN
Float::INFINITY and Float::NAN are both truthy in Ruby. That's expected. What isn't expected, until you've been burned by it, is this:
nan = Float::NAN nan == nan # => false nan.nan? # => true (use this to check)
Float::NAN == Float::NAN is false. This is standard IEEE 754 behavior and it surprises everyone the first time. The check you want is .nan?, not ==.
Float Precision Caps Out at 2^53
Ruby's Float is an IEEE 754 64-bit double-precision number. It carries 15 to 17 significant decimal digits and a maximum value around 1.8 x 10^308. Those are big numbers. Just not as big as Ruby's integers.
The danger is converting a large arbitrary-precision integer to a float. Floats cannot represent integers above 2^53 exactly. Values above that get rounded to the nearest representable float.
a = 2 ** 53 + 1 b = 2 ** 53 + 2 a == b # => false (different integers) a.to_f == b.to_f # => true (same float, precision lost)
Extremely large integers become Infinity when converted:
(10 ** 400).to_f # => Infinity
This matters in graph or DP problems where you initialize a distance table with a large sentinel value. If you mix integer and float arithmetic for that sentinel, you can produce incorrect comparisons that pass sanity checks but fail edge cases. Keep your sentinel as an integer. Float::INFINITY is fine for purely float graphs. Do not convert back and forth across the 2^53 boundary.
Two Ways to Parse Numbers, Very Different Results
to_i and to_f are lenient. They stop at the first character they can't parse and return whatever they found. If they found nothing at all, they return zero.
"123".to_i # => 123 "123abc".to_i # => 123 (silently drops the "abc") "abc".to_i # => 0 (silently returns zero)
Integer() and Float() are strict. They raise ArgumentError if the entire string isn't a valid number.
Integer("123") # => 123 Integer("123abc") # ArgumentError: invalid value for Integer(): "123abc" Integer("abc") # ArgumentError: invalid value for Integer(): "abc"
Use to_i for input you know is clean, and Integer() when you need to validate. The lenient versions have produced production bugs that took days to track down, because "abc".to_i returns 0 without complaint and your code keeps running. Wrong. Quietly. Happy.
to_i in a nutshell.
The Rules at a Glance
| Expression | Result |
|---|---|
(2 ** 100) | Exact integer, no overflow |
-7 / 2 | -4 (floors toward negative infinity) |
7 / -2 | -4 |
-17 % 5 | 3 (sign follows divisor) |
-17.remainder(5) | -2 (sign follows dividend) |
42 / 0 | ZeroDivisionError |
42.0 / 0.0 | Infinity |
0.0 / 0.0 | NaN |
Float::NAN == Float::NAN | false |
"123abc".to_i | 123 |
Integer("123abc") | ArgumentError |
(2**53 + 1).to_f == (2**53 + 2).to_f | true (precision lost) |
(10 ** 400).to_f | Infinity |
Methods Worth Knowing
divmod returns both the quotient and the modulo in one call. The quotient follows Ruby's floored division rule.
17.divmod(5) # => [3, 2] -17.divmod(5) # => [-4, 3]
pow with a modulus argument computes modular exponentiation efficiently in a single call. This matters for cryptography-style problems where computing (base ** exponent) % mod naively would overflow even Ruby's patience.
base.pow(exponent, mod) # computes (base ** exponent) % mod efficiently
digits decomposes an integer into its digits in a given base, least significant first.
255.digits(16) # => [15, 15] (0xff in hex, LSB first)
bit_length tells you how many bits are needed to represent the integer. Useful for sizing operations without converting to strings.
255.bit_length # => 8 256.bit_length # => 9
32-Bit Semantics Still Apply
Even though Ruby integers don't overflow, interview problems are typically designed with 32-bit or 64-bit integers in mind. The problem authors wrote the constraints before they thought about what language you'd be using. The values 2**31 - 1 and 2**63 - 1 still matter.
Problems may explicitly constrain input to those ranges. Algorithms ported from other languages may behave differently once float conversions cross those boundaries. Bitwise problems often assume 32-bit integer semantics, and Ruby does not automatically truncate bit operations to 32 bits. A mask may be necessary.
def to_32bit(n) n &= 0xFFFFFFFF n >= 0x80000000 ? n - 0x100000000 : n end
This comes up in problems like "Reverse Bits" or "Number of 1 Bits" when you need to match Java or C++ behavior exactly. Without this, your bit operations run on arbitrary-precision integers and produce answers that are technically correct and completely wrong for the problem.
Practice It Under Pressure
Knowing the language rules is necessary. Applying them under time pressure while talking out loud is a different skill entirely. A lot of Ruby candidates know about % vs remainder abstractly but reach for the wrong one the moment there's a person watching them type.
SpaceComplexity runs voice-based DSA mock interviews where you talk through your reasoning, not just type. If you're practicing Ruby for interviews, the sessions will surface exactly the places where your number instincts are calibrated to a different language.
More Ruby interview patterns in Ruby for Coding Interviews and Ruby Coding Interview Gotchas. For the general picture across languages, see Integer Overflow in Coding Interviews and Floating Point Precision.