Basics

116. Math.clamp() and Exact Arithmetic in Java

Math.clamp() safely bounds values within a range, while Math.addExact() and multiplyExact() prevent silent integer overflow by throwing ArithmeticException.

Java 21 (java.lang.Math)

Explanation

Two perennial source bugs in numeric Java code are: 1. Unchecked Integer Overflow: In standard Java, Integer.MAX_VALUE + 1 wraps around to Integer.MIN_VALUE silently without throwing any warning or error. If calculating financial balances or allocation buffer sizes, this wrap-around leads to catastrophic security flaws (e.g. allocating negative or tiny arrays). 2. Manual Clamping Logic: Restricting a number to [min, max] historically required awkward nested ternary expressions: Math.max(min, Math.min(val, max)), which is error-prone and inverted easily.

Java 21 introduces Math.clamp() for int, long, float, and double: Math.clamp(value, min, max) cleanly returns min if value < min, max if value > max, and value otherwise, throwing IllegalArgumentException if min > max.

For overflow protection, java.lang.Math provides the *Exact family: addExact(), subtractExact(), multiplyExact(), incrementExact(), and toIntExact(), which throw ArithmeticException upon overflow.

Example

public class MathExactAndClamp {
    public static void main(String[] args) {
        // 1. Math.clamp (Java 21)
        int volume = 120;
        int safeVolume = Math.clamp(volume, 0, 100);
        System.out.println("Clamped volume: " + safeVolume); // 100

        double temperature = -15.4;
        double clampedTemp = Math.clamp(temperature, 0.0, 40.0);
        System.out.println("Clamped temp: " + clampedTemp); // 0.0

        // 2. Silent overflow vs Exact arithmetic
        int max = Integer.MAX_VALUE;
        int silentWrap = max + 1;
        System.out.println("Silent wrap-around: " + silentWrap); // -2147483648 (DANGEROUS)

        try {
            Math.addExact(max, 1); // Safe!
        } catch (ArithmeticException e) {
            System.out.println("Caught overflow cleanly: " + e.getMessage());
        }

        // 3. Safe narrowing conversion
        long huge = 5_000_000_000L;
        try {
            int downcast = Math.toIntExact(huge);
        } catch (ArithmeticException e) {
            System.out.println("toIntExact detected overflow on downcast: " + e.getMessage());
        }
    }
}

Sources