skip to content

In Ruby, why does -7 % 3 return 2 while -7.remainder(3) returns -1, and when does the difference matter?

level: middleimportance: should knowfreq 36%

answer

  1. % follows the divisor's sign
  2. remainder follows the receiver's sign
  3. % pairs with floored /
  4. remainder pairs with truncation
  5. wrap-around index stays in 0...n

basics

~10 s

Integer#% is a modulo whose result takes the divisor's sign and pairs with floored division, so -7 % 3 is 2; Integer#remainder takes the receiver's sign and pairs with truncation, so -7.remainder(3) is -1.

solid answer

~40 s

Ruby offers two answers to "what is left over". `%` (also spelled `modulo`) is defined so that `a == (a / b) * b + a % b` with Ruby's floored `/`: `-7 / 3` is `-3`, so `-7 % 3` is `2`, and a non-zero result always has the divisor's sign. `remainder` is defined against truncating division: `-7` truncated by `3` is `-2`, leaving `-1`, so the result has the receiver's sign. With positive operands they agree. The difference matters for wrap-around arithmetic: `(index - 1) % size` is always in `0...size` for a positive size, which is what you want for rotating through seats at a table or through days of the week, whereas `remainder` can go negative and index from the end of an array by accident.

code

ruby · 11 lines
ruby
-7 % 3            # => 2
-7.modulo(3)      # => 2
-7.remainder(3)   # => -1
7 % -3            # => -2
7.remainder(-3)   # => 1
-7.divmod(3)      # => [-3, 2]

seats = %w[Ana Ben Cy Dee Eli]
payer = 0
payer = (payer - 1) % seats.size   # => 4
seats[payer]                        # => "Eli"

go deeper

for a junior

Recall that Ruby's % result takes the divisor's sign, so -7 % 3 is 2, and that remainder is the method that keeps the receiver's sign.

for a middle

Explain the pairing: % goes with floored / and divmod, remainder goes with truncation, and show the arithmetic for -7 by 3.

for a senior

Spot wrap-around, bucketing and parity code that silently misbehaves on negative inputs, and require tests that exercise negative values.

for a principal

Encourage helpers such as odd? and divmod over hand-rolled sign logic, so negative-number semantics are decided by the language, not by each author.

Ruby has two operations that compute "what is left after division", and they disagree whenever a sign is negative. Interviewers ask this to see whether you know which one `%` is. ## The two definitions - **Modulo (`%`, `modulo`)**: the result has the **same sign as the divisor** (or is zero). It is the partner of Ruby's floored `/`, so `a == (a / b) * b + a % b` always holds. - **Remainder (`remainder`)**: the result has the **same sign as the receiver** (the dividend) (or is zero). It is the partner of truncating division, which rounds the quotient toward zero. `Integer#divmod` returns `[a / b, a % b]`, the floored quotient with the modulo - not the remainder. ## Side by side | Expression | `%` / `modulo` | `remainder` | `divmod` | |---|---|---|---| | `7` by `3` | `1` | `1` | `[2, 1]` | | `-7` by `3` | `2` | `-1` | `[-3, 2]` | | `7` by `-3` | `-2` | `1` | `[-3, -2]` | | `-7` by `-3` | `-1` | `-1` | `[2, -1]` | Walking through `-7` by `3`: 1. Floored division: `-7 / 3` is `-3` (because -2.33 rounds down to -3). 2. Modulo: `-7 - (-3 * 3)` is `-7 + 9`, which is `2`. 3. Truncated division: -2.33 rounds toward zero to `-2`. 4. Remainder: `-7 - (-2 * 3)` is `-7 + 6`, which is `-1`. Floats follow the same rules: `-7.0 % 3` is `2.0`, and `-7.0.remainder(3)` is `-1.0`. ## When the difference matters **Wrap-around indexing.** Rotating who pays the next restaurant bill around a table of five diners, moving backwards: ```ruby seats = %w[Ana Ben Cy Dee Eli] payer = 0 payer = (payer - 1) % seats.size # => 4, wraps to the last seat seats[payer] # => "Eli" ``` With a positive divisor, `%` always lands in `0...size`, so the index is valid. `(payer - 1).remainder(seats.size)` would give `-1` here; that happens to index the last element of an Array, but only by accident, and the same value would be wrong as a key into a Hash or as a database offset. **Parity checks.** `n % 2 == 1` is true for every odd Integer in Ruby, including `-3`, because `-3 % 2` is `1`. `n.remainder(2) == 1` is false for `-3`, whose remainder is `-1`. The clearest option is `n.odd?`, which avoids the question entirely. **Clock and calendar arithmetic.** Converting a negative offset in minutes into hours and minutes with `divmod(60)` gives a non-negative minute part, which is what a time display needs: `-90.divmod(60)` is `[-2, 30]`, meaning two hours back plus thirty minutes forward. **Signed differences.** When the sign of the leftover itself carries meaning - "how far past the last full unit, in the direction we were moving" - `remainder` is the right tool, because it keeps the dividend's sign. ## Zero and other edge cases - `7 % 0` and `7.remainder(0)` raise `ZeroDivisionError` for Integers. - `7.0 % 0` also raises `ZeroDivisionError`: Float modulo checks for a zero divisor even though Float division by zero returns `Infinity`. - `%` on a String is a different method altogether: `String#%` formats text, which is why it appears in the list of operator methods classes define for their own meaning. ## Where each one appears in practice `%` is by far the more common: cyclic schedules (whose turn it is to pay, which day of a seven-day rota), hashing a key into one of `n` buckets, checking divisibility with `(n % 3).zero?`, and splitting a number into units with `divmod`. `remainder` shows up mostly when porting a formula that was written for truncating arithmetic, where copying `%` would silently change the results for negative inputs. When you port such a formula, check which convention the source assumed before choosing the method. ## Summary checklist 1. `%` equals `modulo`, takes the divisor's sign, and pairs with `/` and `divmod`. 2. `remainder` takes the receiver's sign and pairs with truncation. 3. For wrap-around arithmetic with a positive size, use `%`. 4. For parity, use `odd?` and `even?`. 5. Test negative inputs explicitly; positive-only tests cannot tell the two apart.

  • In Ruby, is n % 2 == 1 a safe odd check for negative Integers?
    Yes in Ruby, because `%` takes the divisor's sign: `-3 % 2` is `1`. The check that breaks for negatives is `n.remainder(2) == 1`, since `-3.remainder(2)` is `-1`. `Integer#odd?` states the intent directly and sidesteps both.
  • What does -90.divmod(60) return in Ruby, and how do you read it?
    `[-2, 30]`. The quotient is floored to `-2` and the modulus takes the divisor's positive sign, so the pair reads as "two units back, then thirty forward": `-2 * 60 + 30` is `-90`. `divmod` always pairs the floored quotient with `%`, never with `remainder`.

saying these in an interview costs you the question

  • In Ruby, -7 % 3 is -1 because % keeps the dividend's sign.
  • % and remainder are aliases of the same method in Ruby.
  • divmod returns the quotient together with remainder's result.
  • Integer#modulo differs from % for negative numbers.
  • (i - 1).remainder(size) is the safe way to wrap an index.