Guide Developer Tools 5 min read

Programming languages round a negative division three different ways

A remainder depends on how a language rounds the quotient, so with negative numbers JavaScript, Python and Euclidean division can disagree. Floating point and a zero divisor bring their own surprises.

Maya Lin
Digital Platforms Analyst
Published 5 Nov 2026, 11:09 PM (SGT)
Share:
A round brown wall clock with gold numerals 1 to 12, hanging on an olive-green wall. A round brown wall clock with gold numerals 1 to 12, hanging on an olive-green wall. Photo by Marcelo Mora on Pexels
Advertisement

The modulo operator gives the remainder after dividing one whole number by another, and once a negative number is involved, languages disagree about it. We ran 300 small integer pairs through the three common rules for a remainder, and they disagreed on 129. Every disagreement involved a negative number and a remainder that was not zero. Divide −7 by 3 and JavaScript leaves −1 while Python leaves 2. The third rule, Euclidean division, agrees with Python until the divisor is negative: 7 by −3 leaves 1 where Python leaves −2, and −7 by −3 leaves 2 where Python leaves −1.

Toward zero in JavaScript, C, Java, Go, Rust and PHP

All three rules start from the identity a = m·q + r. The quotient q is rounded if a/m isn't a whole number, and the remainder r is what's left over. Daan Leijen's paper Division and Modulus for Computer Scientists (2001, posted by Microsoft Research) names the options. Truncated division rounds toward zero, and Leijen notes: "The sign of the modulus is always the same as the sign of the dividend."

The ECMAScript 2027 draft defines % with "truncatedQuotient" in its Number::remainder. C11, in the last public draft, WG14 N1570 of April 2011, keeps "the algebraic quotient with any fractional part discarded", which its foreword credits to C99 as "reliable integer division". PHP's manual gives % the sign of the dividend. We ran -7 % 3 in Node 26.13.0, PHP 8.5.12 and C11 under Apple clang, and all three printed −1. Java, Go and Rust specify the same rounding (spec only; not run).

Downward in Python, Ruby and Excel

Floored division rounds the quotient down, so −7 / 3 becomes −3 and the remainder is 2. Leijen writes that this definition "is described by Knuth", and that the remainder then takes the sign of the divisor. Python's expressions reference says its % "always yields a result with the same sign as its second operand (or zero)". Python 3.9.6 and Ruby 2.6.10 both printed 2.

Microsoft gives Excel's MOD function as n − d·INT(n/d), with examples MOD(−3, 2) = 1 and MOD(3, −2) = −1; we did not run Excel. Java's Math.floorMod is floored too, and its own documentation gives floorMod(+4, -3) == -2, so it is no never-negative remainder (spec only). Python itself holds both rules: its Decimal type truncates, and Decimal(-7) % Decimal(3) printed −1.

Never negative under the Euclidean rule

Leijen defines Euclidean division by 0 ≤ r < |m|, whatever the signs. Rust's i32::rem_euclid documents exactly that, "0 <= r < abs(rhs)" (spec only). On our 300 pairs (|a| up to 12, m from ±1 to ±6), floored and Euclidean matched on every positive divisor and parted on 86 pairs, all with a negative divisor. A comparison that only tries -7 % 3 sees two answers and misses the third rule.

Advertisement

Our modulo calculator gives the Euclidean remainder as its headline and, in a revision dated 5 November 2026, adds a table of all three. Enter a = 7 and m = −3 and it reads 1 truncated, −2 floored and 1 Euclidean. Until that revision its modular power could lose exactness once the modulus passed about 94.9 million, the square root of 253, where two residues can multiply past what a double holds exactly (our guide to 53-bit limits covers why). For 1234567891000 mod 1,000,000,007 it returned 236,784,232; Python's pow gives 620,139,939. It also rounded inputs beyond 253 silently and read "0x1A" as 0. The revised version works in BigInt, whose own % is truncated as well: -7n % 3n is −1n in Node.

Back to the floored answer through the usual repair idiom

The fix usually offered is ((a % m) + m) % m. In JavaScript, on the same 300 pairs, it matched the floored rule on all 300 and the Euclidean rule on 214. With a negative divisor it stays negative; ((7 % -3) + -3) % -3 is −2 in Node. For a never-negative answer, Rust has rem_euclid, and in Python a % abs(m) gives it, which we checked on the grid. Go's spec shows a narrower case: for a power-of-two divisor, -11 % 4 is −3 while the bitmask -11 & 3 is 1 (spec only; Node and Python also gave 1). That is the non-negative answer for a power-of-two divisor, and floored division gives it too.

Clear of negative indexes in the RFC 3339 weekday formula

Count four days back from Monday, day 1 with Sunday as 0. In Node, (1 - 4) % 7 is −3 and days[-3] is undefined; in Python 3.9.6 the same expression is 4, Thursday.

RFC 3339 (July 2002) prints a C day-of-week function in its Appendix B, "loosely based on Zeller's Congruence", that ends with cent / 4 + 5 * cent) % 7. Every term is non-negative before C's truncating %. Run in C against an independent calendar, it was right on all 292,500 days from 1 March 1600 to 31 December 2400. We then swapped + 5 * cent for - 2 * cent, equal mod 7. That variant produced a negative index on 14,776 days (5.05%) and was right on 277,724. Wrapping its sum in a Euclidean mod brought the errors back to zero. The RFC does not say why it chose +5; the variant is our experiment.

Nearly a whole divisor left over in floating point

0.3 % 0.1 printed 0.09999999999999998 in Node and Python, and C's fmod returned the same double. PHP's echo fmod(0.3, 0.1) prints 0.1; var_dump shows the long value. The stored 0.3 is 0.29999999999999998889… and the stored 0.1 is 0.10000000000000000555…, so the exact ratio sits just under 3, truncates to 2, and leaves almost a whole 0.1.

We computed (k/10) % 0.1 for k from 1 to 100: 82 results came within 10−9 of 0.1 and 18 within 10−9 of zero. Python's math.remainder, which rounds the quotient to "the closest integer", stayed within 1.25 × 10−15 of zero for all 100.

Nowhere at all when the divisor is zero

We ran 7 by zero where we could; the other rows come from documentation.

OperationDivisor zero givesSource
JavaScript %NaNran, Node 26.13.0
JavaScript BigInt %RangeErrorran, Node 26.13.0
C %undefined behaviourN1570 §6.5.5
C fmodNaN on our build; implementation-definedran, clang C11
Python %ZeroDivisionErrorran, 3.9.6
PHP %DivisionByZeroErrorran, 8.5.12
Ruby %ZeroDivisionErrorran, 2.6.10
Java %ArithmeticExceptionspec only, JLS SE 21
Go %run-time panicspec only
Rust rem_euclidpanicspec only
Excel MOD#DIV/0!documentation only

Division by zero isn't the only edge case. The most negative integer divided by −1 also breaks, because its true quotient is one larger than the type can store. C11 calls both a/b and a%b undefined when the quotient is not representable. The Java and Go specifications set that remainder to 0 (spec only), PHP 8.5.12 printed 0 for PHP_INT_MIN % -1, and Rust's documentation says rem_euclid will panic.

Advertisement
Maya Lin
Digital Platforms Analyst

Maya Lin covers SaaS platforms, workflow automation, creator tools, and productivity software for RECATOOLS.

View author profile → · Editorial policy

About this byline Maya Lin is a RECATOOLS editorial persona used for platform and productivity coverage. Articles are produced and reviewed under RECATOOLS editorial supervision.

Corrections policy

Advertisement