Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Coding Interview Patterns  Module 18 – Maths for coding rounds

Division, remainders and overflow across languages

What -7 / 2 and -7 % 2 give in Python, JavaScript, Java, C++ and Go, how to get floor division and remainders that are never negative, and how to stop overflow.

  • Beginner
  • 30 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25.0.4.1, Go 1.27.1 and Clang 21.0.0
  • By MySmartCoPilot
Free: 30 minutes

What you will learn

  • Predict the quotient and remainder of a negative division in Python, JavaScript, Java, C++ and Go
  • Write floor division, ceiling division and a remainder from 0 to m − 1 in each of the five languages
  • Detect overflow before it happens, with an estimate or a checked operation
  • Choose big integers when an exact answer outgrows 64 bits

Before you start

What you’ll learn · Before you start

What you will learn

  • Predict the quotient and remainder of a negative division in Python, JavaScript, Java, C++ and Go
  • Write floor division, ceiling division and a remainder from 0 to m − 1 in each of the five languages
  • Detect overflow before it happens, with an estimate or a checked operation
  • Choose big integers when an exact answer outgrows 64 bits

Before you start

On this page

Integer division has to round whenever the answer is not whole, and the five languages of this track do not all round the same way. Python rounds the quotient down: -7 // 2 is −4 and -7 % 2 is 1. Java, C++ and Go round towards zero: -7 / 2 is −3 and -7 % 2 is −1, and JavaScript gives the same with Math.trunc(-7 / 2) and %. When a result is too large for its type, the five part ways again: Python’s integers never overflow, Java and Go wrap around without a word, signed overflow in C++ is undefined behaviour, and a JavaScript Number stops holding every integer exactly above 2⁵³ − 1. This lesson runs the same divisions and edge cases in all five languages, side by side, then gives the fixes that hold in each: floor and ceiling division, a remainder from 0 to m − 1, a mean that cannot overflow, overflow checks and big integers.

Two ways to round a quotient

When b does not divide a, the true quotient has a fraction, and integer division must turn it into a whole number. Two rules are common. Truncation drops the fraction, which moves the quotient towards zero: −3.5 becomes −3. Floor division rounds down, towards minus infinity: −3.5 becomes −4. For operands of the same sign the two give the same answer, because 3.5 truncated and 3.5 rounded down are both 3. They differ only when exactly one operand is negative and the division leaves a remainder.

The remainder is never chosen on its own. Every language here defines it so that quotient and remainder rebuild the dividend, a = q × b + r, so the remainder follows from the quotient: r = a − q × b. With truncation, −7 − (−3 × 2) = −1, and the remainder has the sign of the dividend. With floor division, −7 − (−4 × 2) = 1, and the remainder has the sign of the divisor.

−7 ÷ 2 is −3.5. Rounded towards zero it is −3 with remainder −1; rounded down it is −4 with remainder 1.−7 ÷ 2 = −3.5not whole, so it must roundTowards zero: −3Java, C++, Go, JavaScriptremainder−7 − (−3 × 2) = −1sign of the dividendDown: −4Pythonremainder−7 − (−4 × 2) = 1sign of the divisortruncatefloor

One division, two roundings, two remainders

Text description of the diagram

The diagram shows one box at the top with two arrows leading down to two boxes side by side.

1. The top box reads: −7 ÷ 2 = −3.5, which is not a whole number, so an integer quotient must round. 2. The arrow labelled "truncate" leads to the box "Towards zero: −3", used by Java, C++, Go and JavaScript. The remainder that goes with it is −7 − (−3 × 2) = −1, which has the sign of the dividend, −7. 3. The arrow labelled "floor" leads to the box "Down: −4", used by Python. The remainder that goes with it is −7 − (−4 × 2) = 1, which has the sign of the divisor, 2.

The language specifications split the five like this:

  • Python floors. Its reference says that floor division of integers applies the floor function to the mathematical quotient, and that % returns a result with the sign of its second operand, so that x == (x // y) * y + x % y always holds. divmod(a, b) returns both.
  • Java truncates: its specification says that integer division rounds toward 0, and that (a / b) * b + (a % b) equals a.
  • C++ truncates as well: the fractional part of the quotient is discarded.
  • Go truncates, and its specification calls it truncated division: x = q*y + r with |r| < |y|.
  • JavaScript has no integer division for Numbers. -7 / 2 is −3.5, and you choose the rounding with Math.trunc or Math.floor. Its % truncates, so the remainder has the sign of the dividend. BigInt division truncates too, so -7n / 2n is -3n.

A third convention, the Euclidean remainder, is never negative: it lies from 0 to |b| − 1 whatever the signs. No operator of the five uses it, but it is what “a mod m” means in mathematics. For a positive divisor it equals the floor remainder, which is why Python’s % and Java’s Math.floorMod give it whenever m is positive. Go’s math/big package offers both families: Quo and Rem truncate like Go’s operators, while Div and Mod are Euclidean.

The same divisions in five languages

Here are four divisions, one for each pair of signs, and three edge cases, in Python and in JavaScript, where BigInt takes over the 64-bit rows:

Four divisions and three edge cases, in Python and JavaScript

Python · table/table.py

# Division, remainder and three edge cases with Python's int.

pairs = [(7, 2), (-7, 2), (7, -2), (-7, -2)]
for a, b in pairs:
    print(f"{a} // {b} = {a // b}, {a} % {b} = {a % b}")

# The 64-bit limits, where Java's long, C++'s long long and Go's int64 run out.
largest, smallest = 2**63 - 1, -(2**63)
print("largest + 1 =", largest + 1)
print("smallest // -1 =", smallest // -1)

try:
    print(7 // 0)
except ZeroDivisionError as error:
    print("7 // 0 raises ZeroDivisionError:", error)

Output

7 // 2 = 3, 7 % 2 = 1
-7 // 2 = -4, -7 % 2 = 1
7 // -2 = -4, 7 % -2 = -1
-7 // -2 = 3, -7 % -2 = -1
largest + 1 = 9223372036854775808
smallest // -1 = 9223372036854775808
7 // 0 raises ZeroDivisionError: division by zero

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 table.py

JavaScript · table/table.mjs

// Division, remainder and three edge cases in JavaScript: Numbers, then BigInt.

const pairs = [[7, 2], [-7, 2], [7, -2], [-7, -2]];
for (const [a, b] of pairs) {
  console.log(`Math.trunc(${a} / ${b}) = ${Math.trunc(a / b)}, ${a} % ${b} = ${a % b}`);
}
for (const [a, b] of pairs) {
  const x = BigInt(a);
  const y = BigInt(b);
  console.log(`${x}n / ${y}n = ${x / y}n, ${x}n % ${y}n = ${x % y}n`);
}

// A Number holds every integer exactly only up to 2^53 - 1, so the 64-bit limits need BigInt.
console.log('MAX_SAFE_INTEGER + 2 =', Number.MAX_SAFE_INTEGER + 2);
const largest = 2n ** 63n - 1n;
const smallest = -(2n ** 63n);
console.log(`largest + 1n = ${largest + 1n}n`);
console.log(`smallest / -1n = ${smallest / -1n}n`);

console.log(`7 / 0 = ${7 / 0}, 7 % 0 = ${7 % 0}`);
try {
  console.log(7n / 0n);
} catch (error) {
  console.log('7n / 0n throws', error.name);
}

Output

Math.trunc(7 / 2) = 3, 7 % 2 = 1
Math.trunc(-7 / 2) = -3, -7 % 2 = -1
Math.trunc(7 / -2) = -3, 7 % -2 = 1
Math.trunc(-7 / -2) = 3, -7 % -2 = -1
7n / 2n = 3n, 7n % 2n = 1n
-7n / 2n = -3n, -7n % 2n = -1n
7n / -2n = -3n, 7n % -2n = 1n
-7n / -2n = 3n, -7n % -2n = -1n
MAX_SAFE_INTEGER + 2 = 9007199254740992
largest + 1n = 9223372036854775808n
smallest / -1n = 9223372036854775808n
7 / 0 = Infinity, 7 % 0 = NaN
7n / 0n throws RangeError

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node table.mjs

Here is the same program in Java, C++ and Go, built and run with the track’s pinned versions. The C++ build turns on the undefined-behaviour sanitizer, which checks the arithmetic while the program runs and reports each operation whose result the language leaves undefined:

The same divisions in Java, C++ and Go

Java · compiled/Table.java

// Division, remainder and three edge cases with Java's int and long.
class Table {
    public static void main(String[] args) {
        int[][] pairs = {{7, 2}, {-7, 2}, {7, -2}, {-7, -2}};
        for (int[] p : pairs) {
            int a = p[0], b = p[1];
            System.out.println(a + " / " + b + " = " + (a / b) + ", " + a + " % " + b + " = " + (a % b));
        }

        long largest = Long.MAX_VALUE, smallest = Long.MIN_VALUE;
        System.out.println("largest + 1 = " + (largest + 1));
        System.out.println("smallest / -1 = " + (smallest / -1));

        int zero = 0;
        try {
            System.out.println(7 / zero);
        } catch (ArithmeticException e) {
            System.out.println("7 / 0 throws ArithmeticException: " + e.getMessage());
        }
    }
}

C++ · compiled/table.cpp

// Division, remainder and three edge cases with C++'s int and long long.
#include <climits>
#include <iostream>

int main() {
    std::cout << std::unitbuf;  // print each line at once, so it comes before the sanitizer's report

    int pairs[4][2] = {{7, 2}, {-7, 2}, {7, -2}, {-7, -2}};
    for (auto [a, b] : pairs) {
        std::cout << a << " / " << b << " = " << a / b << ", " << a << " % " << b << " = " << a % b << '\n';
    }

    // volatile: the values are read when the program runs, so the compiler cannot work the results out in advance.
    // Each result is computed before its line is printed, so a sanitizer report comes just above that line.
    volatile long long largest = LLONG_MAX, smallest = LLONG_MIN, zero = 0;
    long long sum = largest + 1;
    std::cout << "largest + 1 = " << sum << '\n';
    long long quotient = smallest / -1;
    std::cout << "smallest / -1 = " << quotient << '\n';
    long long by_zero = 7 / zero;
    std::cout << "7 / 0 = " << by_zero << '\n';
}

Go · compiled/table.go

// Division, remainder and three edge cases with Go's int and int64.
package main

import (
	"fmt"
	"math"
)

func main() {
	pairs := [][2]int{{7, 2}, {-7, 2}, {7, -2}, {-7, -2}}
	for _, p := range pairs {
		a, b := p[0], p[1]
		fmt.Printf("%d / %d = %d, %d %% %d = %d\n", a, b, a/b, a, b, a%b)
	}

	var largest, smallest, minusOne int64 = math.MaxInt64, math.MinInt64, -1
	fmt.Println("largest + 1 =", largest+1)
	fmt.Println("smallest / -1 =", smallest/minusOne)

	divideByZero()
}

// divideByZero divides by a variable that holds 0 (a constant 0 does not compile) and prints the panic it causes.
func divideByZero() {
	defer func() { fmt.Println("7 / 0 panics:", recover()) }()
	zero := 0
	fmt.Println(7 / zero)
}

Build and run all three · compiled/table_session.py

from session import run, versions

# Table.java runs as a single source file, table.cpp is built with the undefined-behaviour sanitizer, and go run
# builds table.go.
versions("java -version", "clang++ --version", "go version")
run("""
java Table.java
clang++ -std=c++23 -Wall -Wextra -fsanitize=undefined table.cpp -o table && ./table
go run table.go
""")

Output

# openjdk version "25.0.4.1" LTS
# Apple clang version 21.0.0 (clang-2100.3.34.2)
# go version go1.27.1 darwin/arm64
$ java Table.java
7 / 2 = 3, 7 % 2 = 1
-7 / 2 = -3, -7 % 2 = -1
7 / -2 = -3, 7 % -2 = 1
-7 / -2 = 3, -7 % -2 = -1
largest + 1 = -9223372036854775808
smallest / -1 = -9223372036854775808
7 / 0 throws ArithmeticException: / by zero
$ clang++ -std=c++23 -Wall -Wextra -fsanitize=undefined table.cpp -o table && ./table
7 / 2 = 3, 7 % 2 = 1
-7 / 2 = -3, -7 % 2 = -1
7 / -2 = -3, 7 % -2 = 1
-7 / -2 = 3, -7 % -2 = -1
table.cpp:16:29: runtime error: signed integer overflow: 9223372036854775807 + 1 cannot be represented in type 'long long'
largest + 1 = -9223372036854775808
table.cpp:18:35: runtime error: division of -9223372036854775808 by -1 cannot be represented in type 'long long'
smallest / -1 = -9223372036854775808
table.cpp:20:27: runtime error: division by zero
7 / 0 = 0
$ go run table.go
7 / 2 = 3, 7 % 2 = 1
-7 / 2 = -3, -7 % 2 = -1
7 / -2 = -3, 7 % -2 = 1
-7 / -2 = 3, -7 % -2 = -1
largest + 1 = -9223372036854775808
smallest / -1 = -9223372036854775808
7 / 0 panics: runtime error: integer divide by zero

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 table_session.py

All five recorded runs agree whenever both operands have the same sign. The pairs with mixed signs are where the languages split, and Python is the one that differs from the other four, Java, C++, Go and JavaScript:

Case Python The other four
−7 ÷ 2 −4 −3
−7 % 2 1 −1
7 % −2 −1 1

JavaScript reaches −3 with Math.trunc(a / b), and BigInt’s / gives the same. So if a solution divides values that can be negative, such as changes in a balance, coordinates or temperatures, the language decides the answer.

At the 64-bit limits, 2⁶³ − 1 and −2⁶³, and at zero, the runs part ways further:

  • Python stays exact: both edge results are 2⁶³, and 7 // 0 raises ZeroDivisionError.
  • JavaScript gives 2⁶³ for both as a BigInt. With Numbers, 7 / 0 is Infinity and 7 % 0 is NaN, and both flow on into later arithmetic without an error; 7n / 0n throws a RangeError.
  • Java wraps both results to −2⁶³ without an error, and 7 / 0 throws an ArithmeticException.
  • C++ leaves all three undefined, and the sanitizer reported each one. The numbers printed after the reports are only what this computer’s processor returned: undefined behaviour carries no promise at all, and another compiler or optimisation level may print something else or stop the program.
  • Go wraps both results to −2⁶³ without a panic, and 7 / 0 panics at run time. It also rejects a division by a constant zero when it compiles, which is why table.go divides by a variable that holds 0.

The most negative value divided by −1 is the case people forget: its true result, 2⁶³, is one more than the largest 64-bit value, so the division itself overflows. Java and Go define the result as the dividend, with a remainder of 0. JavaScript needed BigInt for these rows because a Number holds every integer exactly only up to 2⁵³ − 1: MAX_SAFE_INTEGER + 2 printed 9007199254740992, the same Number as MAX_SAFE_INTEGER + 1.

Floor division and remainders from 0 to m − 1

Many problems need the floor behaviour in a language that truncates: the weekday 10 days before a Monday, a position that moves backwards around a ring, a bucket for a hash that may be negative. Java has it in its library: Math.floorDiv and Math.floorMod for int and long, and, from Java 18, Math.ceilDiv for a quotient rounded up.

In C++, Go and JavaScript you write it yourself. For a remainder from 0 to m − 1 with a positive m, take r = a % m and add m only when r came out negative. At that point r lies between −m and 0, so r + m lies between 0 and m and cannot overflow. Many solutions write the same idea as one expression, ((a % m) + m) % m. In exact arithmetic it gives the same answers, but it adds m even when a % m is already positive, so the sum in the middle can reach 2m − 2. Here are both forms in Java, for the usual modulus 10⁹ + 7 and for 2,147,483,647, the largest int, with Math.floorMod as the reference:

Two ways to a remainder from 0 to m − 1, in Java

Java · compiled/Remainders.java

// A remainder from 0 to m - 1 with Java's int, then floor and ceiling division.
class Remainders {
    // Adds m even when a % m is already positive: the sum can pass Integer.MAX_VALUE.
    static int textbook(int a, int m) {
        return ((a % m) + m) % m;
    }

    // r + m lies between 0 and m when r is negative, so it always fits.
    static int addIfNegative(int a, int m) {
        int r = a % m;
        return r < 0 ? r + m : r;
    }

    public static void main(String[] args) {
        for (int m : new int[] {1_000_000_007, Integer.MAX_VALUE}) {
            for (int a : new int[] {-1, 5}) {
                System.out.printf("a = %d, m = %d: textbook %d, add if negative %d, Math.floorMod %d%n",
                        a, m, textbook(a, m), addIfNegative(a, m), Math.floorMod(a, m));
            }
        }

        System.out.println("Math.floorDiv(-7, 2) = " + Math.floorDiv(-7, 2) + ", Math.floorMod(7, -2) = " + Math.floorMod(7, -2));
        System.out.println("Math.ceilDiv(7, 2) = " + Math.ceilDiv(7, 2) + ", Math.ceilDiv(-7, 2) = " + Math.ceilDiv(-7, 2));
    }
}

Run it · compiled/remainders_session.py

from session import run, versions

versions("java -version")
run("""
java Remainders.java
""")

Output

# openjdk version "25.0.4.1" LTS
$ java Remainders.java
a = -1, m = 1000000007: textbook 1000000006, add if negative 1000000006, Math.floorMod 1000000006
a = 5, m = 1000000007: textbook 5, add if negative 5, Math.floorMod 5
a = -1, m = 2147483647: textbook 2147483646, add if negative 2147483646, Math.floorMod 2147483646
a = 5, m = 2147483647: textbook -2147483644, add if negative 5, Math.floorMod 5
Math.floorDiv(-7, 2) = -4, Math.floorMod(7, -2) = -1
Math.ceilDiv(7, 2) = 4, Math.ceilDiv(-7, 2) = -3

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 remainders_session.py

For 10⁹ + 7 the largest sum, 2 × (10⁹ + 7) − 2 = 2,000,000,012, still fits below the int limit, and every form agrees. For 2,147,483,647, 5 % m + m passes the limit and wraps around, so the one-line form returns −2,147,483,644 for a = 5. The same thing happens in JavaScript one level up, where the sum passes 2⁵³ − 1 and is rounded instead:

The same two forms in JavaScript, checked with BigInt JavaScript · remainders/remainders.mjs
// A remainder from 0 to m - 1 in JavaScript, two ways, against the exact answer that BigInt gives.

const textbook = (a, m) => ((a % m) + m) % m; // adds m even when a % m is already positive
const addIfNegative = (a, m) => {
  const r = a % m;
  return r < 0 ? r + m : r; // r + m lies between 0 and m here, so it never leaves the exact range
};
const exact = (a, m) => Number(((BigInt(a) % BigInt(m)) + BigInt(m)) % BigInt(m));

for (const m of [7, Number.MAX_SAFE_INTEGER]) {
  for (const a of [-1, 6]) {
    console.log(`a = ${a}, m = ${m}: textbook ${textbook(a, m)}, add if negative ${addIfNegative(a, m)}, exact ${exact(a, m)}`);
  }
}

// % keeps the sign of a, even when the remainder is zero.
console.log('-4 % 2 is', -4 % 2, 'and equals 0:', -4 % 2 === 0);
console.log('add if negative, then + 0:', addIfNegative(-4, 2) + 0);

Output

a = -1, m = 7: textbook 6, add if negative 6, exact 6
a = 6, m = 7: textbook 6, add if negative 6, exact 6
a = -1, m = 9007199254740991: textbook 9007199254740990, add if negative 9007199254740990, exact 9007199254740990
a = 6, m = 9007199254740991: textbook 5, add if negative 6, exact 6
-4 % 2 is -0 and equals 0: true
add if negative, then + 0: 0

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node remainders.mjs

With m = 2⁵³ − 1, the one-line form returns 5 for a = 6. The last two lines show a quirk of JavaScript’s %: a remainder of zero keeps the sign of a, as the ECMAScript specification requires, so -4 % 2 is -0. It equals 0 in every comparison, but console.log prints it as -0, and an online judge that compares printed text can mark that as a wrong answer. Adding 0 turns it into a plain 0.

A floor quotient in C++ or Go takes one more step: truncate, then subtract 1 when the division left a remainder and the operands had opposite signs. A ceiling quotient adds 1 instead, when there was a remainder and the signs were the same. The common shortcut for a quotient rounded up, (a + b - 1) / b, is right only for a ≥ 0 and b > 0, and its sum can overflow when a is near the limit. For positive values, a / b, plus 1 when a % b is not 0, has neither problem. In JavaScript, Math.trunc(a / b), Math.floor(a / b) and Math.ceil(a / b) are exact for safe integers: the true quotient of two such numbers is never close enough to a whole number for the rounding of / to reach it.

Python has the floor forms built in, and a ceiling quotient is -(-a // b), exact for any size. What it lacks is a quotient rounded towards zero, which a solution ported from Java needs, or a problem that says the fraction is dropped. int(a / b) is the usual answer, and it holds only while the quotient fits a float exactly:

Truncating in Python: // and int(a / b) compared Python · remainders/truncate.py
# Rounding a quotient towards zero in Python, as Java, C++, Go and JavaScript do.
# // rounds down instead, and a / b is a float, which holds whole numbers exactly only up to 2**53.

for a, b in [(-7, 2), (7, -2), (10**20 + 7, 3), (-(10**20 + 7), 3)]:
    print(f"a = {a}, b = {b}")
    print(f"  a // b     = {a // b}")
    print(f"  int(a / b) = {int(a / b)}")

Output

a = -7, b = 2
  a // b     = -4
  int(a / b) = -3
a = 7, b = -2
  a // b     = -4
  int(a / b) = -3
a = 100000000000000000007, b = 3
  a // b     = 33333333333333333335
  int(a / b) = 33333333333333331968
a = -100000000000000000007, b = 3
  a // b     = -33333333333333333336
  int(a / b) = -33333333333333331968

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 truncate.py

For 10²⁰ + 7 divided by 3, int(a / b) is off by 1,367, because a / b became a float with 53 bits of precision first. For the negative dividend, // rounds the wrong way as well. Exact truncation divides the absolute values with // and then gives the result its sign.

Overflow: estimate first, then check

Overflow gives a wrong number without an error message, so the defence starts before the code: estimate the largest value each variable can hold from the limits in the statement. A 64-bit signed integer holds up to 2⁶³ − 1, about 9.2 × 10¹⁸, so the product of two values up to 10⁹ fits (10¹⁸) and the product of three does not (10²⁷). A 32-bit int holds up to 2,147,483,647, about 2.1 × 10⁹, which the sum of three values of 10⁹ already passes. The lesson on reading constraints works through this estimate for sums; for a product, add the exponents.

Big Number Calculator Multiply the largest values your limits allow, such as 10⁹ × 10⁹ × 10⁹, and compare the size of the result with 9.2 × 10¹⁸.

When the estimate is close, or the values come from somewhere you cannot bound, check the operation itself. Here is the volume of a box whose sides are 10⁹, in Java, C++ and Go:

Catching an overflow in Java, C++ and Go

Java · compiled/Checked.java

import java.math.BigInteger;

// The Exact methods of java.lang.Math throw ArithmeticException where plain operators would wrap around.
class Checked {
    interface Step {
        Object run();
    }

    static void show(String what, Step step) {
        try {
            System.out.println(what + " = " + step.run());
        } catch (ArithmeticException e) {
            System.out.println(what + " throws ArithmeticException: " + e.getMessage());
        }
    }

    public static void main(String[] args) {
        long side = 1_000_000_000L;
        long area = side * side; // 10^18 still fits in a long
        show("multiplyExact(side, side)", () -> Math.multiplyExact(side, side));
        show("area * side", () -> area * side);
        show("multiplyExact(area, side)", () -> Math.multiplyExact(area, side));
        show("BigInteger side^3", () -> BigInteger.valueOf(side).pow(3));

        show("toIntExact(3_000_000_000L)", () -> Math.toIntExact(3_000_000_000L));
        show("Math.abs(Integer.MIN_VALUE)", () -> Math.abs(Integer.MIN_VALUE));
        show("absExact(Integer.MIN_VALUE)", () -> Math.absExact(Integer.MIN_VALUE));
        show("divideExact(Integer.MIN_VALUE, -1)", () -> Math.divideExact(Integer.MIN_VALUE, -1));
    }
}

C++ · compiled/checked.cpp

// __builtin_mul_overflow (GCC and Clang) multiplies, stores the low bits and says whether the true result fitted.
#include <iostream>

int main() {
    long long side = 1'000'000'000, area = 0, volume = 0;
    bool overflowed = __builtin_mul_overflow(side, side, &area);
    std::cout << std::boolalpha << "area = " << area << ", overflowed: " << overflowed << '\n';
    overflowed = __builtin_mul_overflow(area, side, &volume);
    std::cout << "volume overflowed: " << overflowed << " (the low 64 bits hold " << volume << ")\n";
}

Go · compiled/checked.go

// Go has no checked multiplication: test the limit before multiplying, and use math/big beyond 64 bits.
package main

import (
	"fmt"
	"math"
	"math/big"
)

// mulFits reports whether a * b fits in an int64, for a and b that are not negative.
func mulFits(a, b int64) bool {
	return b == 0 || a <= math.MaxInt64/b
}

func main() {
	side := int64(1_000_000_000)
	area := side * side
	fmt.Println("area =", area, "fits:", mulFits(side, side))
	fmt.Printf("volume fits: %v (area * side wraps to %d)\n", mulFits(area, side), area*side)
	volume := new(big.Int).Exp(big.NewInt(side), big.NewInt(3), nil)
	fmt.Println("volume as a big.Int =", volume)
}

Build and run all three · compiled/checks_session.py

from session import run, versions

versions("java -version", "clang++ --version", "go version")
run("""
java Checked.java
clang++ -std=c++23 -Wall -Wextra -fsanitize=undefined checked.cpp -o checked && ./checked
go run checked.go
""")

Output

# openjdk version "25.0.4.1" LTS
# Apple clang version 21.0.0 (clang-2100.3.34.2)
# go version go1.27.1 darwin/arm64
$ java Checked.java
multiplyExact(side, side) = 1000000000000000000
area * side = -6930898827444486144
multiplyExact(area, side) throws ArithmeticException: long overflow
BigInteger side^3 = 1000000000000000000000000000
toIntExact(3_000_000_000L) throws ArithmeticException: integer overflow
Math.abs(Integer.MIN_VALUE) = -2147483648
absExact(Integer.MIN_VALUE) throws ArithmeticException: Overflow to represent absolute value of Integer.MIN_VALUE
divideExact(Integer.MIN_VALUE, -1) throws ArithmeticException: integer overflow
$ clang++ -std=c++23 -Wall -Wextra -fsanitize=undefined checked.cpp -o checked && ./checked
area = 1000000000000000000, overflowed: false
volume overflowed: true (the low 64 bits hold -6930898827444486144)
$ go run checked.go
area = 1000000000000000000 fits: true
volume fits: false (area * side wraps to -6930898827444486144)
volume as a big.Int = 1000000000000000000000000000

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 checks_session.py

  • Java: the Exact methods of Math throw ArithmeticException where the operator would wrap: addExact and multiplyExact for sums and products, toIntExact for a long that must become an int, absExact for the one value whose absolute value does not fit (plain Math.abs(Integer.MIN_VALUE) returns a negative number, as its documentation warns) and divideExact for the most negative value divided by −1.
  • C++: GCC and Clang provide __builtin_add_overflow, __builtin_sub_overflow and __builtin_mul_overflow, which compute the result, store its low bits and return whether the true result fitted. While you practise, build with -fsanitize=undefined, as the table above was, so that a signed overflow is reported where it happens.
  • Go: there is no checked operator, so compare with the limit before multiplying. For values that are not negative, b == 0 || a <= math.MaxInt64/b says that the product fits.
  • Python: there is nothing to check, because its integers have unlimited precision.
  • JavaScript: a Number that grows too large is rounded rather than wrapped, so the question is whether the result is still exact:
A product that JavaScript rounds, and the same product as BigInt JavaScript · checks/checks.mjs
// In JavaScript, a result that is too large is rounded, not wrapped. Number.isSafeInteger says when that can happen.

const a = 999_999_999;
const product = a * a;
console.log('a * a =', product, '- safe integer:', Number.isSafeInteger(product));
console.log('as BigInt:', BigInt(a) * BigInt(a));

const side = 1_000_000_000n;
console.log('side ** 3n =', side ** 3n);
try {
  console.log(side + 1);
} catch (error) {
  console.log('side + 1 throws', error.name, '(BigInt and Number do not mix)');
}

Output

a * a = 999999998000000000 - safe integer: false
as BigInt: 999999998000000001n
side ** 3n = 1000000000000000000000000000n
side + 1 throws TypeError (BigInt and Number do not mix)

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node checks.mjs

999,999,999 squared is 999,999,998,000,000,001, but the Number ends in 000, and Number.isSafeInteger returns false for it. For addition, subtraction and multiplication of safe integers, that test is exact: a result that is a safe integer was computed without rounding, and a true result above 2⁵³ − 1 can only round to a number that fails the test. BigInt gives the exact value, but it never mixes with Numbers in one expression, so convert every operand.

Version note

Math.floorDiv, Math.floorMod, addExact, multiplyExact and toIntExact arrived in Java 8, absExact in Java 15, and ceilDiv and divideExact in Java 18, so all of them run on Java 21 and later. std::midpoint needs C++20, and so does the rule that >> rounds a negative value down: before C++20, shifting a negative value to the right gave an implementation-defined result.

Programmer Calculator (Bitwise) Type 2147483647 + 1 at 32 bits, signed, and see the wrap-around and how far the result overflowed.

A mean of two integers that cannot overflow

The mean of two values rounded down looks like (a + b) / 2, and it overflows whenever a + b does. The usual fix, a + (b - a) / 2, has limits of its own. Here are four pairs of Java int values, with the exact answer worked out in long, and three pairs with C++20’s std::midpoint:

The mean of two ints, three ways, in Java and C++

Java · compiled/Means.java

// The mean of two ints, rounded down, three ways, and the exact answer worked out in long.
class Means {
    public static void main(String[] args) {
        int[][] pairs = {{1_500_000_000, 2_000_000_000}, {-2_000_000_000, 2_000_000_000}, {5, -2}, {-3, -4}};
        for (int[] p : pairs) {
            int a = p[0], b = p[1];
            int plain = (a + b) / 2;
            int fromA = a + (b - a) / 2;
            int halves = (a >> 1) + (b >> 1) + (a & b & 1);
            long exact = Math.floorDiv((long) a + b, 2);
            System.out.printf("a = %d, b = %d%n  (a + b) / 2 = %d, a + (b - a) / 2 = %d, halves = %d, exact = %d%n",
                    a, b, plain, fromA, halves, exact);
        }
    }
}

C++ · compiled/means.cpp

// std::midpoint (C++20) never overflows, but it rounds towards its first argument, so the order matters.
#include <iostream>
#include <numeric>

int main() {
    int pairs[3][2] = {{1'500'000'000, 2'000'000'000}, {5, -2}, {-2, 5}};
    for (auto [a, b] : pairs) {
        int halves = (a >> 1) + (b >> 1) + (a & b & 1);  // >> on a negative int rounds down since C++20
        std::cout << "a = " << a << ", b = " << b << ": std::midpoint(a, b) = " << std::midpoint(a, b)
                  << ", halves = " << halves << '\n';
    }
}

Build and run both · compiled/means_session.py

from session import run, versions

versions("java -version", "clang++ --version")
run("""
java Means.java
clang++ -std=c++23 -Wall -Wextra -fsanitize=undefined means.cpp -o means && ./means
""")

Output

# openjdk version "25.0.4.1" LTS
# Apple clang version 21.0.0 (clang-2100.3.34.2)
$ java Means.java
a = 1500000000, b = 2000000000
  (a + b) / 2 = -397483648, a + (b - a) / 2 = 1750000000, halves = 1750000000, exact = 1750000000
a = -2000000000, b = 2000000000
  (a + b) / 2 = 0, a + (b - a) / 2 = -2147483648, halves = 0, exact = 0
a = 5, b = -2
  (a + b) / 2 = 1, a + (b - a) / 2 = 2, halves = 1, exact = 1
a = -3, b = -4
  (a + b) / 2 = -3, a + (b - a) / 2 = -3, halves = -4, exact = -4
$ clang++ -std=c++23 -Wall -Wextra -fsanitize=undefined means.cpp -o means && ./means
a = 1500000000, b = 2000000000: std::midpoint(a, b) = 1750000000, halves = 1750000000
a = 5, b = -2: std::midpoint(a, b) = 2, halves = 1
a = -2, b = 5: std::midpoint(a, b) = 1, halves = 1

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 means_session.py

  • (a + b) / 2 wraps for 1.5 × 10⁹ and 2 × 10⁹ and returns −397,483,648. For −3 and −4 it does not overflow, but it truncates −3.5 to −3 instead of rounding it down to −4.
  • a + (b - a) / 2 is the midpoint of a binary search, and it is safe there because the indexes satisfy 0 ≤ lo ≤ hi, so hi - lo always fits. For values of opposite signs, b - a itself overflows: −2 × 10⁹ and 2 × 10⁹ give −2,147,483,648 instead of 0. It also rounds towards a rather than down, so 5 and −2 give 2.
  • (a >> 1) + (b >> 1) + (a & b & 1) is right for all four. A right shift by one is floor division by 2 for signed values in Java and Go, in C++ from C++20, and in Python, so each half is rounded down; a & b & 1 adds back the 1 that the two halves lose when both a and b are odd. Each half is at most half the range, so their sum always fits.

std::midpoint never overflows, but it rounds an odd sum towards its first argument: std::midpoint(5, -2) is 2 and std::midpoint(-2, 5) is 1, so the order of the arguments changes the answer. In Python, (a + b) // 2 is already exact. In JavaScript, >> works on 32-bit integers only, so for larger Numbers the halves come from Math.floor(a / 2) and Math.floor(b / 2) instead.

When 64 bits are not enough

Some answers are exact integers far beyond 9.2 × 10¹⁸: the volume of the box above, a factorial larger than 20!, or a count that the problem asks for in full rather than modulo 10⁹ + 7. Four of the five languages have integers of any size for them:

  • Python: the ordinary int.
  • JavaScript: BigInt, written with an n suffix, such as 10n ** 27n. Its / truncates, and it never mixes with Numbers in one expression.
  • Java: java.math.BigInteger, which uses methods instead of operators: a.multiply(b). Its remainder keeps the sign of the dividend like %, while mod always returns a value from 0 to m − 1.
  • Go: math/big, whose big.Int methods write the result into the value they are called on: z.Mul(x, y).
  • C++: nothing in the standard library. GCC and Clang offer __int128 as an extension, up to about 1.7 × 10³⁸, and beyond that a solution stores the digits in a vector and carries by hand.

Use them when the statement asks for an exact large value. When it asks for the answer modulo a number, stay with 64-bit integers and take the remainder after every step: each operation then works on small values, and big integers would only add cost.

Exercise

Exercise · Easy · Python, JavaScript

Three integer helpers that hold at the limits

Write three small helpers, in helpers.py for Python or helpers.mjs for JavaScript. Each one must give the exact answer for every input in its range, the largest and smallest values included.

  • pos_mod(a, m) (JavaScript: posMod) returns the remainder of a divided by m, from 0 to m − 1, for m ≥ 1. It is what Python's a % m gives for a positive m.
  • floor_mean(a, b) (JavaScript: floorMean) returns the mean of a and b rounded down, which is Python's (a + b) // 2.
  • trunc_div(a, b) (JavaScript: truncDiv) returns a divided by b with the fraction dropped, rounding towards zero as Java, C++ and Go do, for b ≠ 0.
pos_mod(-1, 7)       → 6
floor_mean(-3, -4)   → -4
floor_mean(5, -2)    → 1
trunc_div(-7, 2)     → -3
trunc_div(7, -2)     → -3

The ranges. In Python, a, b and m are integers of any size, and every result is an int. In JavaScript, they are Numbers that are safe integers, from −(2⁵³ − 1) to 2⁵³ − 1 (m from 1 to 2⁵³ − 1), and every result is a Number that is never -0.

The sample tests check the examples, the edges of each range and 300 random cases against answers worked out another way: with fractions.Fraction in Python and with BigInt in JavaScript.

Python · Starter code · helpers.py

def pos_mod(a, m):
    """The remainder of a divided by m, from 0 to m - 1 (m >= 1)."""
    # Replace this line with your code.
    return 0


def floor_mean(a, b):
    """The mean of a and b, rounded down."""
    # Replace this line with your code.
    return 0


def trunc_div(a, b):
    """a divided by b, rounded towards zero (b != 0)."""
    # Replace this line with your code.
    return 0
The sample tests · test_helpers.py
import math
import random
from fractions import Fraction

from helpers import floor_mean, pos_mod, trunc_div


def test_prompt_examples():
    """the examples from the prompt"""
    assert pos_mod(-1, 7) == 6
    assert floor_mean(-3, -4) == -4
    assert floor_mean(5, -2) == 1
    assert trunc_div(-7, 2) == -3
    assert trunc_div(7, -2) == -3


def test_pos_mod_from_zero_to_m_minus_one():
    """remainders for a from -4 to 4 with m = 3, and with m = 1"""
    assert [pos_mod(a, 3) for a in range(-4, 5)] == [2, 0, 1, 2, 0, 1, 2, 0, 1]
    assert [pos_mod(a, 1) for a in (-5, 0, 5)] == [0, 0, 0]


def test_floor_mean_rounds_down():
    """means of an odd total round down, for both signs"""
    assert floor_mean(3, 4) == 3
    assert floor_mean(-3, 4) == 0
    assert floor_mean(4, -3) == 0
    assert floor_mean(-1, -2) == -2
    assert floor_mean(6, 6) == 6


def test_trunc_div_all_signs():
    """every sign pair, a quotient between -1 and 0, and exact divisions"""
    assert [trunc_div(7, 2), trunc_div(-7, 2), trunc_div(7, -2), trunc_div(-7, -2)] == [3, -3, -3, 3]
    assert trunc_div(-1, 2) == 0
    assert trunc_div(6, -3) == -2
    assert trunc_div(0, -5) == 0


def test_values_far_above_two_to_the_53():
    """a float holds whole numbers exactly only up to 2**53, so these need integer arithmetic"""
    big = 10**20 + 7
    assert trunc_div(big, 3) == 33333333333333333335
    assert trunc_div(-big, 3) == -33333333333333333335
    assert trunc_div(-(2**63), -1) == 2**63
    assert floor_mean(10**30, 10**30 + 1) == 10**30
    assert floor_mean(-(10**30), -(10**30) - 1) == -(10**30) - 1
    assert pos_mod(-(10**30), 10**9 + 7) == (-(10**30)) % (10**9 + 7)


def test_results_are_ints():
    """every result is an int, never a float"""
    for value in (pos_mod(-1, 7), floor_mean(5, -2), trunc_div(-7, 2), trunc_div(10**20 + 7, 3)):
        assert type(value) is int, f"{value!r} is a {type(value).__name__}"


def test_random_against_fractions():
    """300 random cases of several sizes, against exact arithmetic with fractions.Fraction"""
    rng = random.Random(53)
    for _ in range(300):
        size = rng.choice([10, 10**6, 2**63, 10**30])
        a = rng.randint(-size, size)
        b = rng.randint(-size, size) or 1
        m = rng.randint(1, size)
        assert trunc_div(a, b) == math.trunc(Fraction(a, b)), f"trunc_div({a}, {b})"
        assert floor_mean(a, b) == math.floor(Fraction(a + b, 2)), f"floor_mean({a}, {b})"
        assert pos_mod(a, m) == a - m * math.floor(Fraction(a, m)), f"pos_mod({a}, {m})"

JavaScript · Starter code · helpers.mjs

/** The remainder of a divided by m, from 0 to m - 1 (m >= 1), never -0. */
export function posMod(a, m) {
  // Replace this line with your code.
  return 0;
}

/** The mean of a and b, rounded down, exact for every safe integer. */
export function floorMean(a, b) {
  // Replace this line with your code.
  return 0;
}

/** a divided by b, rounded towards zero (b !== 0), never -0. */
export function truncDiv(a, b) {
  // Replace this line with your code.
  return 0;
}
The sample tests · helpers.test.mjs
import { test, assert } from 'mysmartcopilot:test';
import { floorMean, posMod, truncDiv } from './helpers.mjs';

const MAX = Number.MAX_SAFE_INTEGER; // 2^53 - 1

/** Shows -0 as -0 (String(-0) is "0"). */
const show = (x) => (Object.is(x, -0) ? '-0' : String(x));
/** Equal as Object.is sees it: 0 and -0 differ, and the result must be a Number. */
const same = (actual, expected, call) => assert.ok(typeof actual === 'number' && Object.is(actual, expected), `${call}: expected ${show(expected)}, got ${show(actual)}`);

let seed = 53;
const random = () => (seed = (seed * 48271) % 2147483647) / 2147483647;
const randInt = (lo, hi) => lo + Math.floor(random() * (hi - lo + 1));
/** A safe integer: often next to a boundary, sometimes anywhere from -(2^53 - 1) to 2^53 - 1. */
const pick = () => {
  const v = random() < 0.5
    ? Math.min(MAX, [0, 1, 2, 3, 7, 1e6, 2 ** 31, 2 ** 52, MAX - 5][randInt(0, 8)] + randInt(0, 5))
    : randInt(0, 2 ** 26 - 1) * 2 ** 27 + randInt(0, 2 ** 27 - 1);
  return random() < 0.5 && v !== 0 ? -v : v; // never -0
};

test('the examples from the prompt', () => {
  same(posMod(-1, 7), 6, 'posMod(-1, 7)');
  same(floorMean(-3, -4), -4, 'floorMean(-3, -4)');
  same(floorMean(5, -2), 1, 'floorMean(5, -2)');
  same(truncDiv(-7, 2), -3, 'truncDiv(-7, 2)');
  same(truncDiv(7, -2), -3, 'truncDiv(7, -2)');
});

test('posMod: from 0 to m - 1, and 0 rather than -0', () => {
  const got = [];
  for (let a = -4; a <= 4; a++) got.push(posMod(a, 3));
  assert.deepEqual(got, [2, 0, 1, 2, 0, 1, 2, 0, 1]);
  same(posMod(-4, 2), 0, 'posMod(-4, 2)');
  same(posMod(-5, 1), 0, 'posMod(-5, 1)');
  same(posMod(-MAX, MAX), 0, 'posMod(-MAX_SAFE_INTEGER, MAX_SAFE_INTEGER)');
});

test('posMod with a modulus near 2^53', () => {
  same(posMod(6, MAX), 6, 'posMod(6, MAX_SAFE_INTEGER)');
  same(posMod(MAX - 1, MAX), MAX - 1, 'posMod(MAX_SAFE_INTEGER - 1, MAX_SAFE_INTEGER)');
  same(posMod(-1, MAX), MAX - 1, 'posMod(-1, MAX_SAFE_INTEGER)');
  same(posMod(-MAX, MAX - 1), MAX - 2, 'posMod(-MAX_SAFE_INTEGER, MAX_SAFE_INTEGER - 1)');
});

test('floorMean rounds down, for both signs', () => {
  same(floorMean(3, 4), 3, 'floorMean(3, 4)');
  same(floorMean(-3, 4), 0, 'floorMean(-3, 4)');
  same(floorMean(4, -3), 0, 'floorMean(4, -3)');
  same(floorMean(-1, -2), -2, 'floorMean(-1, -2)');
  same(floorMean(-1, 1), 0, 'floorMean(-1, 1)');
});

test('floorMean where a + b passes 2^53', () => {
  same(floorMean(MAX, MAX - 3), MAX - 2, 'floorMean(MAX_SAFE_INTEGER, MAX_SAFE_INTEGER - 3)');
  same(floorMean(MAX, MAX), MAX, 'floorMean(MAX_SAFE_INTEGER, MAX_SAFE_INTEGER)');
  same(floorMean(-MAX, -MAX), -MAX, 'floorMean(-MAX_SAFE_INTEGER, -MAX_SAFE_INTEGER)');
  same(floorMean(-MAX, -(MAX - 2)), -(MAX - 1), 'floorMean(-MAX_SAFE_INTEGER, -(MAX_SAFE_INTEGER - 2))');
});

test('truncDiv: every sign pair, and 0 rather than -0', () => {
  same(truncDiv(7, 2), 3, 'truncDiv(7, 2)');
  same(truncDiv(-7, -2), 3, 'truncDiv(-7, -2)');
  same(truncDiv(-1, 2), 0, 'truncDiv(-1, 2)');
  same(truncDiv(0, -5), 0, 'truncDiv(0, -5)');
  same(truncDiv(MAX, -1), -MAX, 'truncDiv(MAX_SAFE_INTEGER, -1)');
});

test('300 random cases against BigInt', () => {
  for (let k = 0; k < 300; k++) {
    const a = pick();
    const b = pick() || 1;
    const m = Math.abs(pick()) || 1;
    const [A, B, M] = [BigInt(a), BigInt(b), BigInt(m)];
    same(truncDiv(a, b), Number(A / B), `truncDiv(${a}, ${b})`);
    same(floorMean(a, b), Number((A + B) >> 1n), `floorMean(${a}, ${b})`);
    same(posMod(a, m), Number(((A % M) + M) % M), `posMod(${a}, ${m})`);
  }
});
A hint

Look for the step where an exact value can be lost. In Python it is a / b, which makes a float: divide the absolute values with // instead, then give the result its sign. In JavaScript it is a + b, which can pass 2⁵³ − 1 even when a and b are safe: halve each number first, rounding down, and add 1 back when both were odd. And % keeps the sign of a, so a remainder of zero can come out as -0.

The sample tests run on this device, in your browser (Pyodide, QuickJS): nothing is sent to mysmartcopilot.com. The first run of each language downloads it: Python (about 13.5 MB) or JavaScript (about 0.6 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Check yourself

6 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 6 In Java, what is -17 % 5?

    Choose one answer.

    Show the answer to question 1

    Answer: −2

    Java truncates −17 ÷ 5 = −3.4 to −3, and the remainder rebuilds the dividend: −17 − (−3 × 5) = −2, which has the sign of the dividend. Python rounds −3.4 down to −4 instead, so its -17 % 5 is −17 − (−4 × 5) = 3.

  2. Question 2 of 6 What does Python print for print(-17 // 5)?

    Type a number.

    Show the answer to question 2

    Answer: -4

    // rounds the quotient −3.4 down, towards minus infinity, which gives −4. Java, C++ and Go give −3 for -17 / 5, and so does JavaScript's Math.trunc(-17 / 5).

  3. Question 3 of 6 In Java, m can be any int from 1 to 2,147,483,647 and a any int. Which expressions always give a remainder from 0 to m − 1?

    Choose every answer that is right.

    Show the answer to question 3

    Answer:

    • Math.floorMod(a, m)
    • a % m < 0 ? a % m + m : a % m

    Math.floorMod returns a value with the sign of the divisor, and adding m only to a negative remainder keeps the sum between 0 and m. The one-line form adds m to positive remainders too, and for m above 2³⁰ that sum can wrap: the lesson's run gave −2,147,483,644 for a = 5 and m = 2,147,483,647. Math.abs(a % m) is never negative but has the wrong value, 1 instead of 2 for a = −1 and m = 3. Math.abs(a) is still negative for Integer.MIN_VALUE.

  4. Question 4 of 6 Which Java expression gives the mean of two ints a and b, rounded down, for every pair and without overflow?

    Choose one answer.

    Show the answer to question 4

    Answer: (a >> 1) + (b >> 1) + (a & b & 1)

    (a + b) / 2 overflows for 1.5 × 10⁹ and 2 × 10⁹, and it truncates −3.5 to −3. a + (b - a) / 2 overflows when the signs differ and rounds towards a. (a + b) >>> 1 reads the 32 bits of the sum as an unsigned number: that rescues the midpoint of two indexes that are not negative, but for −3 and −4 it gives 2,147,483,644. Halving each value with >> rounds down, and a & b & 1 adds back the 1 lost when both are odd.

  5. Question 5 of 6 checks.mjs multiplies 999,999,999 by itself as a Number and as a BigInt, cubes 10⁹ as a BigInt, then adds the Number 1 to a BigInt.

    What does this program print? Choose one answer.

    // In JavaScript, a result that is too large is rounded, not wrapped. Number.isSafeInteger says when that can happen.
    
    const a = 999_999_999;
    const product = a * a;
    console.log('a * a =', product, '- safe integer:', Number.isSafeInteger(product));
    console.log('as BigInt:', BigInt(a) * BigInt(a));
    
    const side = 1_000_000_000n;
    console.log('side ** 3n =', side ** 3n);
    try {
      console.log(side + 1);
    } catch (error) {
      console.log('side + 1 throws', error.name, '(BigInt and Number do not mix)');
    }
    Show the answer to question 5

    Answer: it prints

    a * a = 999999998000000000 - safe integer: false
    as BigInt: 999999998000000001n
    side ** 3n = 1000000000000000000000000000n
    side + 1 throws TypeError (BigInt and Number do not mix)

    The true square has 18 digits, more than the 15 to 16 that a Number holds exactly, so it is rounded to the nearest value a double can store, which ends in 000, and it is above 2⁵³ − 1, so it is not a safe integer. BigInt arithmetic is exact at any size. Adding the Number 1 to a BigInt throws a TypeError instead of converting either side.

  6. Question 6 of 6 A box has three sides of up to 10⁹ each. Which Java type holds its volume exactly?

    Choose one answer.

    Show the answer to question 6

    Answer: BigInteger

    The volume can reach 10²⁷, far above the long limit of about 9.2 × 10¹⁸. A double can hold numbers that large, but with 53 bits of precision, about 15 to 17 significant digits, so a volume such as 999,999,999³ is rounded. BigInteger is exact at any size.

Interview questions

In Java, C++, Go and JavaScript, % can return a negative number. How do you get a remainder from 0 to m − 1, and when does the common form ((a % m) + m) % m go wrong? There, % takes the sign of the dividend, so -7 % 3 is −1 rather than 2. Add m only when the remainder came out negative: r = a % m, then r += m if r < 0. At that point r lies between −m and 0, so r + m lies between 0 and m and cannot overflow. Java’s Math.floorMod(a, m) gives the same result for a positive m. The one-line form gives the same answers in exact arithmetic, but it adds m to a remainder that may already be positive, so its sum can reach 2m − 2. For m = 10⁹ + 7 that still fits in a 32-bit int; once m is above 2³⁰ it does not, and Java returns −2,147,483,644 for a = 5 and m = 2,147,483,647. JavaScript Numbers fail the same way for m above 2⁵², where the sum is rounded.

Besides addition and multiplication, which integer operations can overflow in a fixed-width type such as Java’s int? Several that look harmless. Negating the most negative value overflows, because it has no positive counterpart: in Java, -Integer.MIN_VALUE and Math.abs(Integer.MIN_VALUE) both return Integer.MIN_VALUE, which is negative. Dividing that value by −1 overflows too: Java and Go define the quotient as the dividend itself, with a remainder of 0, while in C++ both are undefined behaviour. Subtraction can overflow when the operands have opposite signs, which breaks comparators written as a - b and midpoints written as a + (b - a) / 2. A left shift multiplies by a power of two without any check, so 1 << 31 is −2,147,483,648 in a Java int. A narrowing cast keeps only the low bits: (int) 3_000_000_000L is −1,294,967,296. Java’s Exact methods, such as negateExact, absExact and toIntExact, throw an ArithmeticException in each of these cases instead.

Key takeaways

  • Python floors a quotient; Java, C++, Go and JavaScript (with Math.trunc) truncate it, and the remainder follows from a = q × b + r. The answers differ only when exactly one operand is negative.
  • For a remainder from 0 to m − 1, add m only when a % m is negative, or use Math.floorMod in Java. The one-line ((a % m) + m) % m overflows once m is above half the type’s range.
  • Dividing the most negative value by −1 overflows: Java and Go return the dividend, and C++ leaves it undefined. Division by zero throws or panics everywhere except with JavaScript Numbers.
  • In JavaScript, a remainder of zero can be -0, which console.log prints as -0.
  • Estimate the largest value from the limits before choosing a type, then check the risky operations: the Exact methods in Java, the overflow builtins and the sanitizer in C++, a limit test in Go, Number.isSafeInteger in JavaScript.
  • (a >> 1) + (b >> 1) + (a & b & 1) is a mean rounded down that never overflows; a + (b - a) / 2 is safe only for values of one sign, such as indexes.
  • When an exact answer outgrows 64 bits, use int, BigInt, BigInteger or big.Int; when only a remainder is asked for, stay with 64-bit integers and reduce after every step.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.