arithmetic: companion code for the Add Two Numbers Without Overflow post

Silent int/long overflow reproduced with real numbers, the Math *Exact()
family (addExact/subtractExact/multiplyExact/incrementExact/decrementExact/
negateExact/toIntExact/absExact) with the abs(MIN_VALUE) trap absExact
exists to catch, widening to long vs BigInteger, and the XOR-based overflow
bit trick Math.addExact uses internally, cross-checked against BigInteger
ground truth over 100k random int pairs per JUnit repeat.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01FhzLY5p6okFva3qsnsRyvM
This commit is contained in:
2026-09-30 09:16:49 +00:00
co-authored by Claude Sonnet 5
parent 202f2f18a5
commit af615cc17c
15 changed files with 514 additions and 0 deletions
+1
View File
@@ -12,6 +12,7 @@ article; each module's own README has that article's version table, quickstart,
| [`synchronizers`](synchronizers/) | CountDownLatch vs CyclicBarrier vs Phaser vs Semaphore in Java | | [`synchronizers`](synchronizers/) | CountDownLatch vs CyclicBarrier vs Phaser vs Semaphore in Java |
| [`executors`](executors/) | ExecutorService Done Right: Shutdown, try-with-resources and Virtual Thread Executors | | [`executors`](executors/) | ExecutorService Done Right: Shutdown, try-with-resources and Virtual Thread Executors |
| [`concurrency-interview`](concurrency-interview/) | Top 40 Java Concurrency Interview Questions and Answers (2026) | | [`concurrency-interview`](concurrency-interview/) | Top 40 Java Concurrency Interview Questions and Answers (2026) |
| [`arithmetic`](arithmetic/) | Add Two Numbers in Java Without Overflow: addExact, Widening, and BigInteger |
## License ## License
+47
View File
@@ -0,0 +1,47 @@
# arithmetic
Companion code for the ankurm.com post *"Add Two Numbers in Java Without Overflow."* Eighth
module in `java-core-examples`.
`a + b` on two Java `int`s never throws when the true mathematical result doesn't fit - it wraps
around silently, two's-complement style. This module reproduces that wraparound with real
numbers, then works through every real fix: the `Math` class's `*Exact()` family (which throws
`ArithmeticException` instead of wrapping), widening to `long`, `BigInteger` for unbounded
precision, and the actual bit trick `Math.addExact` uses internally to detect overflow without
doing the addition twice at different widths.
## Versions this was built and tested against
| Component | Version | Notes |
|---|---|---|
| JDK | 25.0.4.1+1 (Temurin, LTS) | No preview flags needed - every API used here is finalized. |
| JUnit Jupiter | 5.11.0 | Includes a 100,000-iteration randomized cross-check per repeat. |
| Maven | 3.9.11 | |
| Hardware | 2 vCPU x86-64 VM | Same sandbox as the rest of this series. |
## Quickstart
```bash
export JAVA_HOME=/path/to/jdk-25
mvn package
java -cp target/classes com.ankurm.arithmetic.OverflowWrapDemo
java -cp target/classes com.ankurm.arithmetic.ExactArithmeticDemo
```
`scripts/run-all.sh` regenerates every file in `output/` (needs `JDK25_HOME`).
## What's in here
| File | What it shows |
|---|---|
| `.../OverflowWrapDemo.java` | `int` and `long` addition and multiplication wrapping silently past their max values - no exception, just a wrong answer. |
| `.../ExactArithmeticDemo.java` | The `Math` `*Exact()` family: `addExact`, `subtractExact`, `multiplyExact`, `incrementExact`, `decrementExact`, `negateExact`, `toIntExact`, `absExact` - normal case and overflow-throws case for each, plus the `Math.abs(Integer.MIN_VALUE)` trap that `absExact` exists to catch. |
| `.../WideningAndBigIntegerDemo.java` | Widening to `long` fixes the `int`-range overflow (but `long` can still overflow); `BigInteger` never overflows at all. |
| `.../OverflowBitTrickDemo.java` | The XOR-based overflow test for addition, cross-checked against `Math.addExact()` on the same inputs. |
| `src/test/.../ArithmeticClaimsTest.java` | Asserts every claim above, including a 100,000-sample-per-repeat randomized cross-check of the bit-trick detector against `BigInteger` ground truth. |
| `output/01`-`04` | Each demo's real run. |
| `output/05` | JUnit correctness run. |
## License
MIT - see the [repo-wide LICENSE](../LICENSE).
+16
View File
@@ -0,0 +1,16 @@
$ java OverflowWrapDemo
=== int addition wraps silently past Integer.MAX_VALUE ===
Integer.MAX_VALUE = 2147483647
Integer.MAX_VALUE + 1 = -2147483648 (wrapped to Integer.MIN_VALUE, no exception)
Integer.MIN_VALUE = -2147483648
=== A realistic case: summing item prices in cents overflows int ===
priceCents * quantity = 1800000000 * 2 = -694967296 (should be 3,600,000,000 - this is wrong, and no exception was thrown)
=== Multiplication overflows just as silently as addition ===
100000 * 100000 = 1410065408 (true value is 10,000,000,000)
=== long has the same problem, just a much higher ceiling ===
Long.MAX_VALUE = 9223372036854775807
Long.MAX_VALUE + 1 = -9223372036854775808 (wrapped to Long.MIN_VALUE)
+24
View File
@@ -0,0 +1,24 @@
$ java ExactArithmeticDemo
=== addExact / subtractExact / multiplyExact: normal case ===
Math.addExact(20, 22) = 42
Math.subtractExact(50, 8) = 42
Math.multiplyExact(6, 7) = 42
=== addExact / multiplyExact: overflow case - throws instead of wrapping ===
Math.addExact(Integer.MAX_VALUE, 1) threw: integer overflow
Math.multiplyExact(100000, 100000) threw: integer overflow
=== incrementExact / decrementExact / negateExact ===
Math.incrementExact(41) = 42
Math.decrementExact(43) = 42
Math.negateExact(-42) = 42
Math.incrementExact(Integer.MAX_VALUE) threw: integer overflow
=== toIntExact: the safe narrowing cast from long back to int ===
Math.toIntExact(2000000000L) = 2000000000
Math.toIntExact(3000000000L) threw: integer overflow
=== The classic trap: Math.abs(Integer.MIN_VALUE) does NOT throw, and is wrong ===
Math.abs(Integer.MIN_VALUE) = -2147483648 (still negative! +2147483648 doesn't fit in an int)
Math.absExact(Integer.MIN_VALUE) threw: Overflow to represent absolute value of Integer.MIN_VALUE
@@ -0,0 +1,13 @@
$ java WideningAndBigIntegerDemo
=== Widening to long fixes the int-range overflow from OverflowWrapDemo ===
int: Integer.MAX_VALUE + 1 = -2147483648 (wrong)
long: (long) Integer.MAX_VALUE + 1 = 2147483648 (correct)
=== Widening only raises the ceiling - long still overflows eventually ===
Long.MAX_VALUE + 1 = -9223372036854775808 (wrapped to Long.MIN_VALUE - same bug, bigger numbers)
=== BigInteger never overflows - it grows to fit the true value ===
BigInteger.valueOf(Long.MAX_VALUE).add(ONE) = 9223372036854775808
BigInteger.valueOf(2).pow(100) = 1267650600228229401496703205376
(that value has no exact long or int representation at all)
@@ -0,0 +1,15 @@
$ java OverflowBitTrickDemo
=== Hand-rolled overflow detector vs. known cases ===
2000000000 + 2000000000 -> raw int result -294967296, overflow detected: true
2000000000 + -1000000000 -> raw int result 1000000000, overflow detected: false
2147483647 + 1 -> raw int result -2147483648, overflow detected: true
-2147483648 + -1 -> raw int result 2147483647, overflow detected: true
100 + 200 -> raw int result 300, overflow detected: false
=== Cross-checked against Math.addExact() on the same inputs ===
2000000000 + 2000000000: bit-trick says overflow=true, Math.addExact() agrees=true
2000000000 + -1000000000: bit-trick says overflow=false, Math.addExact() agrees=true
2147483647 + 1: bit-trick says overflow=true, Math.addExact() agrees=true
-2147483648 + -1: bit-trick says overflow=true, Math.addExact() agrees=true
100 + 200: bit-trick says overflow=false, Math.addExact() agrees=true
@@ -0,0 +1,4 @@
-------------------------------------------------------------------------------
Test set: com.ankurm.arithmetic.ArithmeticClaimsTest
-------------------------------------------------------------------------------
Tests run: 14, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.438 s -- in com.ankurm.arithmetic.ArithmeticClaimsTest
+43
View File
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ankurm</groupId>
<artifactId>java-core-examples</artifactId>
<version>1.0</version>
</parent>
<artifactId>arithmetic</artifactId>
<name>arithmetic</name>
<description>Adding two numbers in Java without silent overflow: the wraparound bug reproduced, the Math *Exact() family, widening to long, BigInteger, and the bit trick Math.addExact itself uses to detect overflow.</description>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<release>25</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
</plugin>
</plugins>
</build>
</project>
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
# Regenerates every file in ../output/ from a real run against $JDK25_HOME.
# Usage: JDK25_HOME=/path/to/jdk-25 ./run-all.sh
set -euo pipefail
if [ -z "${JDK25_HOME:-}" ]; then
echo "Set JDK25_HOME to a JDK 25 install first." >&2
exit 1
fi
cd "$(dirname "$0")/.."
OUT="output"
mkdir -p "$OUT"
"$JDK25_HOME/bin/mvn" -q -f pom.xml package 2>/dev/null || true
CP="target/classes"
if [ ! -d "$CP" ]; then
mkdir -p target/classes
"$JDK25_HOME/bin/javac" -d target/classes $(find src/main/java -name '*.java')
fi
run() {
local cls="$1" out="$2"
echo "\$ java $cls" > "$OUT/$out"
echo >> "$OUT/$out"
"$JDK25_HOME/bin/java" -cp "$CP" "com.ankurm.arithmetic.$cls" >> "$OUT/$out" 2>&1
}
run OverflowWrapDemo 01-overflow-wrap.txt
run ExactArithmeticDemo 02-exact-arithmetic.txt
run WideningAndBigIntegerDemo 03-widening-and-biginteger.txt
run OverflowBitTrickDemo 04-overflow-bit-trick.txt
mkdir -p target/test-classes
"$JDK25_HOME/bin/javac" -cp "target/classes:$(find / -name 'junit-jupiter-*.jar' 2>/dev/null | tr '\n' ':')" \
-d target/test-classes $(find src/test/java -name '*.java') 2>/dev/null || true
"$JDK25_HOME/bin/mvn" -q -f pom.xml test > "$OUT/05-correctness-tests.txt" 2>&1 || true
cp target/surefire-reports/com.ankurm.arithmetic.ArithmeticClaimsTest.txt "$OUT/05-correctness-tests.txt" 2>/dev/null || true
echo "Done. See $OUT/"
@@ -0,0 +1,66 @@
package com.ankurm.arithmetic;
/**
* The {@code Math} class carries a whole family of "exact" arithmetic methods: every one of them
* does the normal operation when the true mathematical result fits the return type, and throws
* {@link ArithmeticException} instead of silently wrapping when it doesn't. This demo runs the
* addition-relevant members of that family - both the "it just works" case and the "it throws"
* case - plus one genuinely surprising edge case: {@code Math.abs(Integer.MIN_VALUE)} does NOT
* throw and does NOT return a positive number, because positive {@code Integer.MIN_VALUE} has no
* representation in a 32-bit int. {@code Math.absExact} exists specifically to catch that.
*/
public class ExactArithmeticDemo {
public static void main(String[] args) {
System.out.println("=== addExact / subtractExact / multiplyExact: normal case ===");
System.out.println("Math.addExact(20, 22) = " + Math.addExact(20, 22));
System.out.println("Math.subtractExact(50, 8) = " + Math.subtractExact(50, 8));
System.out.println("Math.multiplyExact(6, 7) = " + Math.multiplyExact(6, 7));
System.out.println();
System.out.println("=== addExact / multiplyExact: overflow case - throws instead of wrapping ===");
try {
Math.addExact(Integer.MAX_VALUE, 1);
} catch (ArithmeticException e) {
System.out.println("Math.addExact(Integer.MAX_VALUE, 1) threw: " + e.getMessage());
}
try {
Math.multiplyExact(100_000, 100_000);
} catch (ArithmeticException e) {
System.out.println("Math.multiplyExact(100000, 100000) threw: " + e.getMessage());
}
System.out.println();
System.out.println("=== incrementExact / decrementExact / negateExact ===");
System.out.println("Math.incrementExact(41) = " + Math.incrementExact(41));
System.out.println("Math.decrementExact(43) = " + Math.decrementExact(43));
System.out.println("Math.negateExact(-42) = " + Math.negateExact(-42));
try {
Math.incrementExact(Integer.MAX_VALUE);
} catch (ArithmeticException e) {
System.out.println("Math.incrementExact(Integer.MAX_VALUE) threw: " + e.getMessage());
}
System.out.println();
System.out.println("=== toIntExact: the safe narrowing cast from long back to int ===");
long fitsInInt = 2_000_000_000L;
System.out.println("Math.toIntExact(2000000000L) = " + Math.toIntExact(fitsInInt));
long tooBigForInt = 3_000_000_000L;
try {
Math.toIntExact(tooBigForInt);
} catch (ArithmeticException e) {
System.out.println("Math.toIntExact(3000000000L) threw: " + e.getMessage());
}
System.out.println();
System.out.println("=== The classic trap: Math.abs(Integer.MIN_VALUE) does NOT throw, and is wrong ===");
int minValueAbs = Math.abs(Integer.MIN_VALUE);
System.out.println("Math.abs(Integer.MIN_VALUE) = " + minValueAbs
+ " (still negative! +2147483648 doesn't fit in an int)");
try {
Math.absExact(Integer.MIN_VALUE);
} catch (ArithmeticException e) {
System.out.println("Math.absExact(Integer.MIN_VALUE) threw: " + e.getMessage());
}
}
}
@@ -0,0 +1,60 @@
package com.ankurm.arithmetic;
/**
* How does {@code Math.addExact} actually know overflow happened, without doing the addition
* twice at different widths? A bit trick: for {@code int x + int y = result}, overflow occurred
* if and only if {@code x} and {@code y} have the same sign AND that sign differs from
* {@code result}'s sign. Two numbers with the same sign can only overflow into the opposite
* sign; two numbers with different signs can never overflow (their sum is always between them).
*
* <p>Expressed without any branching on sign:
* {@code overflow = ((x ^ result) & (y ^ result)) < 0}. XOR-ing two ints and checking the sign
* bit of the result is a cheap way to ask "do these differ in sign?" - the expression is true
* (negative, i.e. the top bit is set) exactly when both {@code x} and {@code y} differ in sign
* from {@code result}, which is exactly the overflow condition above. This is not a novel trick -
* it is functionally the same check the JDK's own {@code Math.addExact} source uses internally.
*/
public class OverflowBitTrickDemo {
/** Returns true if x + y overflows the int range, computed without widening to long. */
static boolean additionOverflows(int x, int y) {
int result = x + y; // allowed to wrap; we only use it to test the sign relationship
return ((x ^ result) & (y ^ result)) < 0;
}
public static void main(String[] args) {
System.out.println("=== Hand-rolled overflow detector vs. known cases ===");
report(2_000_000_000, 2_000_000_000); // same sign, overflows
report(2_000_000_000, -1_000_000_000); // different signs, never overflows
report(Integer.MAX_VALUE, 1); // classic edge case
report(Integer.MIN_VALUE, -1); // negative-side overflow
report(100, 200); // ordinary case, no overflow
System.out.println();
System.out.println("=== Cross-checked against Math.addExact() on the same inputs ===");
int[][] cases = {
{2_000_000_000, 2_000_000_000},
{2_000_000_000, -1_000_000_000},
{Integer.MAX_VALUE, 1},
{Integer.MIN_VALUE, -1},
{100, 200}
};
for (int[] c : cases) {
boolean detected = additionOverflows(c[0], c[1]);
boolean addExactThrew;
try {
Math.addExact(c[0], c[1]);
addExactThrew = false;
} catch (ArithmeticException e) {
addExactThrew = true;
}
System.out.println(c[0] + " + " + c[1] + ": bit-trick says overflow=" + detected
+ ", Math.addExact() agrees=" + (detected == addExactThrew));
}
}
private static void report(int x, int y) {
boolean overflow = additionOverflows(x, y);
System.out.println(x + " + " + y + " -> raw int result " + (x + y) + ", overflow detected: " + overflow);
}
}
@@ -0,0 +1,42 @@
package com.ankurm.arithmetic;
/**
* Java's {@code int} and {@code long} arithmetic never throws on overflow. It wraps around
* silently, two's-complement style, and the compiler and JVM both consider {@code a + b} on
* two ints to be perfectly well-defined behavior even when the mathematically correct result
* doesn't fit. This demo reproduces that wraparound with real numbers instead of just quoting
* {@code Integer.MAX_VALUE}.
*/
public class OverflowWrapDemo {
public static void main(String[] args) {
System.out.println("=== int addition wraps silently past Integer.MAX_VALUE ===");
int max = Integer.MAX_VALUE;
int wrapped = max + 1;
System.out.println("Integer.MAX_VALUE = " + max);
System.out.println("Integer.MAX_VALUE + 1 = " + wrapped + " (wrapped to Integer.MIN_VALUE, no exception)");
System.out.println("Integer.MIN_VALUE = " + Integer.MIN_VALUE);
System.out.println();
System.out.println("=== A realistic case: summing item prices in cents overflows int ===");
int priceCents = 1_800_000_000; // $18,000,000.00, a plausible large invoice line in cents
int quantity = 2;
int totalCents = priceCents * quantity;
System.out.println("priceCents * quantity = " + priceCents + " * " + quantity + " = " + totalCents
+ " (should be 3,600,000,000 - this is wrong, and no exception was thrown)");
System.out.println();
System.out.println("=== Multiplication overflows just as silently as addition ===");
int a = 100_000;
int b = 100_000;
int product = a * b; // mathematically 10,000,000,000 - doesn't fit in an int
System.out.println(a + " * " + b + " = " + product + " (true value is 10,000,000,000)");
System.out.println();
System.out.println("=== long has the same problem, just a much higher ceiling ===");
long longMax = Long.MAX_VALUE;
long longWrapped = longMax + 1;
System.out.println("Long.MAX_VALUE = " + longMax);
System.out.println("Long.MAX_VALUE + 1 = " + longWrapped + " (wrapped to Long.MIN_VALUE)");
}
}
@@ -0,0 +1,35 @@
package com.ankurm.arithmetic;
import java.math.BigInteger;
/**
* Two more ways to avoid the overflow in {@link OverflowWrapDemo}: widen to {@code long} before
* adding (cheap, but only pushes the ceiling higher - it can still overflow), or switch to
* {@link BigInteger} for genuinely unbounded precision (more expensive, never overflows).
*/
public class WideningAndBigIntegerDemo {
public static void main(String[] args) {
System.out.println("=== Widening to long fixes the int-range overflow from OverflowWrapDemo ===");
int a = Integer.MAX_VALUE;
int bad = a + 1; // wraps, wrong
long good = (long) a + 1; // widened before the add, correct
System.out.println("int: Integer.MAX_VALUE + 1 = " + bad + " (wrong)");
System.out.println("long: (long) Integer.MAX_VALUE + 1 = " + good + " (correct)");
System.out.println();
System.out.println("=== Widening only raises the ceiling - long still overflows eventually ===");
long lmax = Long.MAX_VALUE;
long stillWraps = lmax + 1;
System.out.println("Long.MAX_VALUE + 1 = " + stillWraps + " (wrapped to Long.MIN_VALUE - same bug, bigger numbers)");
System.out.println();
System.out.println("=== BigInteger never overflows - it grows to fit the true value ===");
BigInteger big = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE);
System.out.println("BigInteger.valueOf(Long.MAX_VALUE).add(ONE) = " + big);
BigInteger huge = BigInteger.valueOf(2).pow(100);
System.out.println("BigInteger.valueOf(2).pow(100) = " + huge);
System.out.println("(that value has no exact long or int representation at all)");
}
}
@@ -0,0 +1,106 @@
package com.ankurm.arithmetic;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import java.math.BigInteger;
import java.util.Random;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ArithmeticClaimsTest {
@Test
void plainIntAdditionWrapsPastMaxValueInsteadOfThrowing() {
int wrapped = Integer.MAX_VALUE + 1;
assertEquals(Integer.MIN_VALUE, wrapped);
}
@Test
void addExactThrowsExactlyWhenTheTrueSumDoesNotFitAnInt() {
assertEquals(42, Math.addExact(20, 22));
assertThrows(ArithmeticException.class, () -> Math.addExact(Integer.MAX_VALUE, 1));
}
@Test
void multiplyExactThrowsExactlyWhenTheTrueProductDoesNotFitAnInt() {
assertEquals(42, Math.multiplyExact(6, 7));
assertThrows(ArithmeticException.class, () -> Math.multiplyExact(100_000, 100_000));
}
@Test
void absDoesNotThrowOnIntegerMinValueAndReturnsAWrongNegativeNumber() {
// Math.abs is NOT part of the exact family - this is the trap absExact exists to catch.
assertEquals(Integer.MIN_VALUE, Math.abs(Integer.MIN_VALUE));
}
@Test
void absExactThrowsOnIntegerMinValueInsteadOfReturningAWrongAnswer() {
assertThrows(ArithmeticException.class, () -> Math.absExact(Integer.MIN_VALUE));
}
@Test
void toIntExactThrowsWhenALongDoesNotFitInAnInt() {
assertEquals(2_000_000_000, Math.toIntExact(2_000_000_000L));
assertThrows(ArithmeticException.class, () -> Math.toIntExact(3_000_000_000L));
}
@Test
void wideningToLongBeforeAddingAvoidsTheIntRangeOverflow() {
int a = Integer.MAX_VALUE;
long widened = (long) a + 1;
assertEquals(2_147_483_648L, widened);
}
@Test
void bigIntegerNeverOverflowsRegardlessOfMagnitude() {
BigInteger huge = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE);
assertEquals(new BigInteger("9223372036854775808"), huge);
}
@RepeatedTest(5)
void bitTrickOverflowDetectorAgreesWithBigIntegerGroundTruthOnRandomPairs() {
Random random = new Random();
for (int i = 0; i < 100_000; i++) {
int x = random.nextInt();
int y = random.nextInt();
boolean detected = OverflowBitTrickDemo.additionOverflows(x, y);
BigInteger trueSum = BigInteger.valueOf(x).add(BigInteger.valueOf(y));
boolean actuallyOverflows = trueSum.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0
|| trueSum.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0;
assertEquals(actuallyOverflows, detected,
() -> x + " + " + y + ": bit trick said overflow=" + detected
+ " but true sum is " + trueSum);
}
}
@Test
void bitTrickAgreesWithMathAddExactOnKnownEdgeCases() {
int[][] cases = {
{2_000_000_000, 2_000_000_000},
{2_000_000_000, -1_000_000_000},
{Integer.MAX_VALUE, 1},
{Integer.MIN_VALUE, -1},
{Integer.MIN_VALUE, Integer.MIN_VALUE},
{100, 200},
{0, 0}
};
for (int[] c : cases) {
boolean detected = OverflowBitTrickDemo.additionOverflows(c[0], c[1]);
boolean addExactThrew = true;
try {
Math.addExact(c[0], c[1]);
addExactThrew = false;
} catch (ArithmeticException ignored) {
// expected on overflow
}
assertTrue(detected == addExactThrew,
c[0] + " + " + c[1] + ": bit trick=" + detected + " vs addExact threw=" + addExactThrew);
}
}
}
+1
View File
@@ -20,6 +20,7 @@
<module>synchronizers</module> <module>synchronizers</module>
<module>executors</module> <module>executors</module>
<module>concurrency-interview</module> <module>concurrency-interview</module>
<module>arithmetic</module>
</modules> </modules>
<properties> <properties>