Contents

Math.abs(), Math.min(), and Math.max() are overloaded for int, long, float, and double. Math.signum() returns -1.0, 0.0, or 1.0 based on the sign of a double. Java 21 adds Math.clamp() to constrain a value within a range in one call:

// abs — works for int, long, float, double System.out.println(Math.abs(-42)); // 42 System.out.println(Math.abs(-3.14)); // 3.14 System.out.println(Math.abs(0)); // 0 // Edge case: abs(Integer.MIN_VALUE) overflows and returns a negative number System.out.println(Math.abs(Integer.MIN_VALUE)); // -2147483648 (same value!) // Use Math.absExact() (Java 15+) to throw ArithmeticException instead // Math.absExact(Integer.MIN_VALUE); // throws ArithmeticException // min and max — overloaded for all numeric primitives int smaller = Math.min(10, 20); // 10 long larger = Math.max(100L, 200L); // 200 double minD = Math.min(3.14, 2.71); // 2.71 // Chaining min/max for clamping (pre-Java 21) int value = 150; int clamped = Math.min(Math.max(value, 0), 100); // clamp to [0, 100] → 100 // Math.clamp() — Java 21+, cleaner clamping int result = Math.clamp(value, 0, 100); // 100 long lRes = Math.clamp(300L, 0L, 255L); // 255 double dRes = Math.clamp(-1.5, 0.0, 1.0); // 0.0 // signum — returns -1.0, 0.0, or 1.0 System.out.println(Math.signum(-42.0)); // -1.0 System.out.println(Math.signum(0.0)); // 0.0 System.out.println(Math.signum(99.0)); // 1.0 // copySign — apply the sign of one value to the magnitude of another double pos = Math.copySign(5.0, -1.0); // -5.0 (magnitude 5, sign of -1) double neg = Math.copySign(5.0, 1.0); // 5.0

Math.pow(base, exp) computes arbitrary powers as a double. Math.sqrt() and Math.cbrt() provide square and cube roots. Math.hypot(x, y) computes the hypotenuse of a right triangle as √(x²+y²) without intermediate overflow:

// pow — base raised to the exponent, always returns double double squared = Math.pow(3, 2); // 9.0 double cubed = Math.pow(2, 10); // 1024.0 double fraction = Math.pow(8, 1.0/3); // 2.0 (cube root via pow) // Special pow cases System.out.println(Math.pow(0, 0)); // 1.0 (defined by IEEE 754) System.out.println(Math.pow(-1, 0.5)); // NaN (not a real number) System.out.println(Math.pow(Double.POSITIVE_INFINITY, 2)); // Infinity // sqrt — square root double root = Math.sqrt(25.0); // 5.0 double sqr2 = Math.sqrt(2.0); // 1.4142135623730951 System.out.println(Math.sqrt(-1)); // NaN // cbrt — cube root (handles negative inputs correctly) double cbrt = Math.cbrt(27.0); // 3.0 double cbrtNeg = Math.cbrt(-8.0); // -2.0 (unlike pow, cbrt works with negatives) // hypot — Euclidean distance, avoids overflow for large values double hyp = Math.hypot(3.0, 4.0); // 5.0 (Pythagorean triple) double hyp2 = Math.hypot(1e154, 1e154); // ~1.414e154 (no overflow; pow(1e154,2) would overflow) // Integer power without double — use repeated multiplication or BigInteger.pow() long pow2_32 = 1L << 32; // 4294967296 — fast integer powers of 2 // For arbitrary int exponents: long intPow(long base, int exp) { long result = 1; for (int i = 0; i < exp; i++) result *= base; return result; } // exp — e raised to the power x double eToX = Math.exp(1); // 2.718281828459045 (e¹) double eNeg = Math.exp(-1); // 0.36787944117144233 // expm1 — exp(x) - 1, more accurate for small x double small = Math.expm1(0.0001); // ~0.00010000500016667084 (more precise than exp(0.0001)-1)

Java provides four rounding functions with distinct semantics: floor rounds toward negative infinity, ceil toward positive infinity, round uses half-up rounding and returns an integer type, and rint rounds to the nearest even integer (banker's rounding) and returns a double:

double pos = 2.7; double neg = -2.7; double half = 2.5; // floor — round toward negative infinity (always ≤ input) System.out.println(Math.floor(pos)); // 2.0 System.out.println(Math.floor(neg)); // -3.0 System.out.println(Math.floor(2.0)); // 2.0 // ceil — round toward positive infinity (always ≥ input) System.out.println(Math.ceil(pos)); // 3.0 System.out.println(Math.ceil(neg)); // -2.0 System.out.println(Math.ceil(2.0)); // 2.0 // round — half-up, returns int (for float) or long (for double) System.out.println(Math.round(2.4)); // 2 (long) System.out.println(Math.round(2.5)); // 3 (half rounds up) System.out.println(Math.round(-2.5)); // -2 (half rounds toward +∞) System.out.println(Math.round(-2.6)); // -3 // rint — banker's rounding (round-half-to-even), returns double System.out.println(Math.rint(2.5)); // 2.0 (2 is even, rounds down) System.out.println(Math.rint(3.5)); // 4.0 (4 is even, rounds up) System.out.println(Math.rint(2.4)); // 2.0 System.out.println(Math.rint(2.6)); // 3.0 // Truncation toward zero — cast to int int truncated = (int) 2.9; // 2 int negTrunc = (int) -2.9; // -2 (toward zero, not toward -∞ like floor) // Rounding to N decimal places double value = 3.14159; double rounded2dp = Math.round(value * 100.0) / 100.0; // 3.14 // Prefer BigDecimal.setScale() for financial rounding accuracy // floorDiv and floorMod — integer division that rounds toward -∞ System.out.println(Math.floorDiv(7, 2)); // 3 (same as 7/2 for positives) System.out.println(Math.floorDiv(-7, 2)); // -4 (7/2=-3 rounds down to -4) System.out.println(Math.floorMod(7, 3)); // 1 System.out.println(Math.floorMod(-7, 3)); // 2 (always non-negative when divisor > 0) Use Math.floorMod() instead of % when you need a non-negative remainder — the % operator can return negative values for negative dividends, which breaks modular arithmetic (e.g., wrapping array indices or clock arithmetic).

All trigonometric functions in Math work in radians, not degrees. Use Math.toRadians() and Math.toDegrees() to convert. Math.log() computes the natural logarithm, Math.log10() the base-10 logarithm, and Math.log1p() is numerically stable for values close to zero:

// Constants System.out.println(Math.PI); // 3.141592653589793 System.out.println(Math.E); // 2.718281828459045 // Degree/radian conversion double deg = 90.0; double rad = Math.toRadians(deg); // 1.5707963267948966 (π/2) double backToDeg = Math.toDegrees(Math.PI); // 180.0 // Trigonometric functions (input in radians) System.out.println(Math.sin(Math.PI / 2)); // 1.0 System.out.println(Math.cos(Math.PI)); // -1.0 (not exactly, ~-1.0) System.out.println(Math.tan(Math.PI / 4)); // 1.0 (45°) // Inverse trig — return radians double angle = Math.asin(1.0); // π/2 = 1.5707... (arcsin of 1) double aCos = Math.acos(0.0); // π/2 double aTan = Math.atan(1.0); // π/4 // atan2(y, x) — angle of vector (x, y) from origin, handles quadrant correctly double theta = Math.atan2(1.0, 1.0); // π/4 = 45° double theta2 = Math.atan2(-1.0, -1.0); // -3π/4 = -135° // Hyperbolic functions double sinh = Math.sinh(1.0); // (e¹ - e⁻¹) / 2 double cosh = Math.cosh(1.0); // (e¹ + e⁻¹) / 2 double tanh = Math.tanh(1.0); // sinh/cosh // Logarithms System.out.println(Math.log(Math.E)); // 1.0 (natural log: ln(e) = 1) System.out.println(Math.log(1.0)); // 0.0 System.out.println(Math.log(0.0)); // -Infinity System.out.println(Math.log(-1.0)); // NaN System.out.println(Math.log10(1000.0)); // 3.0 System.out.println(Math.log10(1.0)); // 0.0 // log1p(x) — ln(1+x), accurate for tiny x where log(1+x) loses precision double tiny = 1e-15; double accurate = Math.log1p(tiny); // much more accurate than Math.log(1 + tiny) // Change of base: log_b(x) = log(x) / log(b) double log2of8 = Math.log(8) / Math.log(2); // 3.0

Math.random() returns a double in [0.0, 1.0). It is backed by a single global Random instance, which becomes a contention point in multi-threaded code. ThreadLocalRandom gives each thread its own generator with no contention and a richer API — it is the preferred choice for concurrent applications:

import java.util.Random; import java.util.concurrent.ThreadLocalRandom; // Math.random() — double in [0.0, 1.0) double rand = Math.random(); // e.g., 0.7342... // Scale to a range [min, max) double randomInRange = min + (max - min) * Math.random(); // Random integer in [0, n) — common pattern int n = 10; int randInt = (int) (Math.random() * n); // 0..9 // java.util.Random — more control, seedable Random rng = new Random(); // non-deterministic seed Random seeded = new Random(42L); // deterministic seed (for testing/reproducibility) seeded.nextInt(); // any int seeded.nextInt(100); // int in [0, 100) seeded.nextLong(); // any long seeded.nextDouble(); // double in [0.0, 1.0) seeded.nextBoolean(); // true or false seeded.nextGaussian(); // Gaussian-distributed double, mean 0, std 1 // Fill an array with random bytes byte[] bytes = new byte[16]; rng.nextBytes(bytes); // ThreadLocalRandom — preferred for concurrent code (no shared state, no contention) int tRandInt = ThreadLocalRandom.current().nextInt(1, 101); // [1, 100] double tRandD = ThreadLocalRandom.current().nextDouble(0.5, 1.5); // [0.5, 1.5) long tRandL = ThreadLocalRandom.current().nextLong(1_000_000L); // [0, 1_000_000) // Streams of random numbers (Java 8+) ThreadLocalRandom.current() .ints(5, 1, 7) // 5 random ints in [1, 7) (like dice rolls) .forEach(System.out::println); Random rng2 = new Random(); rng2.doubles(10, 0.0, 1.0) // stream of 10 doubles in [0.0, 1.0) .average() .ifPresent(System.out::println); Math.random() and java.util.Random are not cryptographically secure — never use them for passwords, tokens, or security-sensitive values. Use java.security.SecureRandom for cryptographic purposes.

Integer arithmetic in Java silently wraps on overflow — Integer.MAX_VALUE + 1 gives Integer.MIN_VALUE with no exception. The Math.addExact(), subtractExact(), multiplyExact(), and toIntExact() methods throw ArithmeticException on overflow, making overflow bugs detectable rather than silent:

// Silent overflow — the default, dangerous for financial/safety-critical code int max = Integer.MAX_VALUE; // 2_147_483_647 System.out.println(max + 1); // -2_147_483_648 (wraps silently!) long lMax = Long.MAX_VALUE; System.out.println(lMax + 1); // -9_223_372_036_854_775_808 (wraps silently!) // Math.addExact / subtractExact / multiplyExact — throw on overflow try { int safe = Math.addExact(Integer.MAX_VALUE, 1); // throws ArithmeticException } catch (ArithmeticException e) { System.out.println("overflow detected: " + e.getMessage()); // "integer overflow" } int sum = Math.addExact(1_000_000, 2_000_000); // 3_000_000 — no overflow, fine long lSum = Math.addExact(Long.MAX_VALUE - 1, 1L); // OK int diff = Math.subtractExact(Integer.MIN_VALUE + 1, 1); // OK // Math.subtractExact(Integer.MIN_VALUE, 1); // throws long product = Math.multiplyExact(1_000_000L, 1_000_000L); // 1_000_000_000_000 — fine // Math.multiplyExact(Long.MAX_VALUE, 2L); // throws // toIntExact — safely cast long to int long bigLong = 42L; int asInt = Math.toIntExact(bigLong); // 42 — fine // Math.toIntExact(Long.MAX_VALUE); // throws — doesn't fit in int // negateExact — negate with overflow check int negated = Math.negateExact(5); // -5 // Math.negateExact(Integer.MIN_VALUE); // throws (abs value doesn't fit) // absExact (Java 15+) — abs with overflow check int absVal = Math.absExact(-42); // 42 // Math.absExact(Integer.MIN_VALUE); // throws // floorDiv / floorMod — division rounding toward -∞ (consistent sign of remainder) // Standard % gives remainder with sign of dividend: System.out.println( 7 % 3); // 1 System.out.println(-7 % 3); // -1 (may surprise you) System.out.println( 7 % -3); // 1 System.out.println(-7 % -3); // -1 // floorMod always has the sign of the divisor: System.out.println(Math.floorMod(-7, 3)); // 2 (not -1) System.out.println(Math.floorMod( 7, -3)); // -2 (not 1) // Practical: wrapping a circular buffer index int index = -1; int bufferSize = 8; int wrapped = Math.floorMod(index, bufferSize); // 7 (not -1) Use the exact methods (addExact, multiplyExact, etc.) whenever overflow would produce a silently wrong result — e.g., computing prices, timestamps, array indices, or population counts. Silent overflow is one of the most common sources of subtle numeric bugs in Java.