Introduction
Arithmetic is at the core of every program and every programming language in existence. From integer and floating-point arithmetic all the way down to bitwise and pointer arithmetic, these mathematical operations translate into machine instructions which execute with thundering speeds and manipulate the state of software applications used across all sorts of different domains. Most modern programming languages have a dedicated category of errors or exceptions for dealing with all the peculiar cases and problematic conditions that may arise while performing these arithmetic operations.
 
ArithmeticException & Why it is an Unchecked Exception
In Java, any arithmetic operation which creates an exceptional condition makes the Java Virtual Machine throw the ArithmeticException
exception [1]. Generally speaking, anything that a scientific calculator isn’t able to process would throw this exception. At a lower level, certain rules and constraints are imposed by the programming language itself, so if any of these are broken, the ArithmeticException
exception will emerge.
ArithmeticException
inherits from the RuntimeException
class which means it is an unchecked, runtime exception [2]. This is due to a language design decision made to reduce the exception handling clutter that would otherwise arise with the high frequency of arithmetic operations, but more importantly because throwing and propagating arithmetic exceptions wouldn’t make sense for the majority of cases, which in practice are the result of logical programming errors that need to be refactored, rather than exceptions that need to be handled. Consequently, Java doesn’t require ArithmeticException
instances to be checked and will only let them manifest at runtime.
 
How to Handle ArithmeticException
To prevent the ArithmeticException
exception in Java, one should diligently implement methods with arithmetic operations and ensure that they are correct from a mathematical and a semantical standpoint. If and when encountered, the ArithmeticException
exception should instigate refactoring of the problematic code, and only in rare and justified cases, the exception should be explicitly handled.
 
ArithmeticException Examples
Division by zero (integer arithmetic)
Dividing a real number by zero is one of those mathematical operations that seem very simple but do not have a clean and definitive answer. The result of this operation is formally considered to be undefined, as any attempt at a definition leads to a contradiction [3]. Since this is a special case of the division operation, Java treats it as an exceptional condition and throws the ArithmeticException
exception whenever it encounters it at runtime.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package rollbar;
public class DivisionByZero {
public static void main(String... args) {
int a = 50, b = 0;
int c = divideAndSquare(a, b);
System.out.println(c);
}
static int divideAndSquare(int x, int y) {
int z = x / y;
return z * z;
}
}
Exception in thread "main" java.lang.ArithmeticException: / by zero
at rollbar.DivisionByZero.divideAndSquare(DivisionByZero.java:12)
at rollbar.DivisionByZero.main(DivisionByZero.java:7)
 
Preferred approach
The proper way to deal with division by zero is to make sure that the divisor variable is never zero, or when the input cannot be controlled and there is a possibility of zero manifesting itself in the equation, treating that as one of the expected options and resolving it accordingly. This usually means testing (validating) the value of the divisor before using it, as shown below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package rollbar;
public class DivisionByZero {
public static void main(String... args) {
int a = 50, b = 0;
if (b != 0) {
int c = divideAndSquare(a, b);
System.out.println(c);
} else {
System.out.println("undefined (division by zero)");
}
}
static int divideAndSquare(int x, int y) {
int z = x / y;
return z * z;
}
}
undefined (division by zero)
 
Alternative approach
As with any other exception, it is possible to catch the ArithmeticException
inside a try-catch
construct, but this should generally be avoided as it creates memory overhead and understates the importance of validating input values and working with a bounded set of parameters.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package rollbar;
public class DivisionByZero {
public static void main(String... args) {
int a = 50, b = 0;
try {
int c = divideAndSquare(a, b);
System.out.println(c);
} catch (ArithmeticException e) {
System.out.println(e.getMessage());
}
}
static int divideAndSquare(int x, int y) {
int z = x / y;
return z * z;
}
}
/ by zero
 
Division by zero doesn’t always throw ArithmeticException
It is important to be aware that division by zero in the context of floating point numbers does NOT trigger the ArithmeticException
. This is because the IEEE 754 standard [4] defines division by zero in floating point arithmetic as ±Infinity and the JVM specification follows this standard [5]. As it can be seen in the example below, setting the operand types to the double
floating point number type, results in the positive Infinity
constant [6] being assigned to the variable z
, which then multiplied by itself yields Infinity
again.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package rollbar;
public class DivisionByZeroFP {
public static void main(String... args) {
int a = 50, b = 0;
double c = divideAndSquare(a, b);
System.out.println(c);
}
static double divideAndSquare(double x, double y) {
double z = x / y;
return z * z;
}
}
Infinity
 
Non-terminating decimal expansion (floating point arithmetic)
Many Java software applications used in the financial sector or otherwise requiring the representation and manipulation of large numbers with great precision, rely on accurate floating point arithmetic and the use of specialized classes such as BigDecimal
[7]. Working with these classes requires special attention to detail so as to avoid certain mistakes and prevent erroneous results. As an example, failing to specify a scale with a rounding mode to any operation that may produce a number with an infinite decimal expansion (such as 1.3333...) [8] will throw an ArithmeticException
.
1
2
3
4
5
6
7
8
9
10
11
12
13
package rollbar;
import java.math.BigDecimal;
public class NonTerminatingDecimalExpansion {
public static void main(String... args) {
var a = new BigDecimal("1.8");
var b = new BigDecimal("9.2");
var c = a.divide(b);
System.out.println(c);
}
}
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.base/java.math.BigDecimal.divide(BigDecimal.java:1723)
at rollbar.NonTerminatingDecimalExpansion.main(NonTerminatingDecimalExpansion.java:10)
The code in the example above declares two BigDecimal
instances with the values of 1.8 and 9.2, and tries to divide the first one by the second one. However, since the result of 1.8/9.2 is 0.195652173913043478260869565217391304347826086…. with an endlessly repeating sequence, the BigDecimal::divide
method is unable to return an exact value and the JVM throws an ArithmeticException
. As the Java documentation for BigDecimal
suggests:
“...In the case of divide, the exact quotient could have an infinitely long decimal expansion; for example, 1 divided by 3. If the quotient has a nonterminating decimal expansion and the operation is specified to return an exact result, an ArithmeticException is thrown. Otherwise, the exact result of the division is returned, as done for other operations.”
The way out of this issue is to specify a scale of the quotient to be returned and a rounding policy to apply to the calculated result. One of the ways to do this is by invoking the overridden version of the BigDecimal::divide
method which takes two additional parameters for scale and rounding mode, as demonstrated below. Now the resulting computation is a valid number rounded down to 4 decimal places, as explicitly specified.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package rollbar;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class NonTerminatingDecimalExpansion {
public static void main(String... args) {
var a = new BigDecimal("1.8");
var b = new BigDecimal("9.2");
var c = a.divide(b, 4, RoundingMode.DOWN);
System.out.println(c);
}
}
0.1956
 
Safe type casts & putting ArithmeticException to good use
In rare instances, particularly when designing libraries for use by other programs and APIs, ArithmeticException
can effectively be used as a safeguard against undesired results and consequences. One such case is numeric type conversions (aka. casts), which Java allows to be done in either direction, from a smaller capacity type to a larger one and vice versa. Casting from a larger to a smaller capacity type is known as downcasting, which is a process where certain information may be lost if the value is larger than what the smaller type can hold. As a specific example, below is a small program casting the maximum value a long
type can hold to an integer, i.e., int
type. The resulting value here is -1 which is not representative of nor close to the initial value in any way.
1
2
3
4
5
6
7
8
9
10
11
package rollbar;
public class SafeNumericTypeCast {
public static void main(String... args) {
long l = Long.MAX_VALUE;
int i = (int) l;
System.out.println(l);
System.out.println(i);
}
}
9223372036854775807
-1
To avoid this from happening, an explicit check can be performed to see whether the input value falls within the boundaries of what the target type can hold, as shown below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package rollbar;
public class SafeNumericTypeCast {
public static void main(String... args) {
long l = Long.MAX_VALUE;
int i = longToInt(l);
System.out.println(l);
System.out.println(i);
}
static int longToInt(long l) {
if (l > Integer.MAX_VALUE || l < Integer.MIN_VALUE) {
throw new ArithmeticException(l + " cannot be safely cast to int.");
}
return (int) l;
}
}
Exception in thread "main" java.lang.ArithmeticException: 9223372036854775807 cannot be safely cast to int.
at rollbar.SafeNumericTypeCast.longToInt(SafeNumericTypeCast.java:14) at rollbar.SafeNumericTypeCast.main(SafeNumericTypeCast.java:7)
This naive and straightforward approach will prevent unsafe casts by triggering the ArithmeticException
exception, which is a reasonable solution in this scenario. A more succinct and idiomatic way of accomplishing the same would be to use the native Java method Math::toIntExact
which essentially does the same under the hood and makes the type cast safe, i.e., checked.
1
2
3
4
5
6
7
8
9
10
11
package rollbar;
public class SafeNumericTypeCast {
public static void main(String... args) {
long l = Long.MAX_VALUE;
int i = Math.toIntExact(l);
System.out.println(l);
System.out.println(i);
}
}
Exception in thread "main" java.lang.ArithmeticException: integer overflow
at java.base/java.lang.Math.toIntExact(Math.java:1080)
at rollbar.SafeNumericTypeCast.main(SafeNumericTypeCast.java:7)
 
Conclusion
Arithmetic operations are some of the most frequently encountered operations found in programming code. Java has a dedicated type of exception called ArithmeticException
for dealing with exceptional conditions that arise from these operations. The key to preventing the ArithmeticException
is being very explicit and deliberate in dealing with special cases such as integer division by zero and non-terminating decimal expansions in floating point arithmetic. Practical examples of these cases alongside possible ways and mechanisms for dealing with them are presented in this article. Finally, a software design scenario where the ArithmeticException
exception can be used in a purposeful and beneficial manner is explored, in the context of safe numeric type conversions.
 
Track, Analyze and Manage Errors With Rollbar
Managing Java errors and exceptions in your code is challenging. It can make deploying production code an unnerving experience. Being able to track, analyze, and manage errors in real-time can help you to proceed with more confidence. Rollbar automates error monitoring and triaging, making fixing Java errors easier than ever. Sign Up Today!
 
References
[1] Oracle, 2021. ArithmeticException (Java SE 17 & JDK 17). Oracle and/or its affiliates. [Online]. Available: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/ArithmeticException.html. [Accessed Jan. 25, 2022]
[2] Oracle, 2021. Unchecked Exceptions — The Controversy (The Java™ Tutorials > Essential Java Classes > Exceptions). Oracle and/or its affiliates. [Online]. Available: https://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html. [Accessed Jan. 25, 2022]
[3] Mathworld.wolfram.com, 2022. Division by Zero -- from Wolfram MathWorld. Wolfram Research, Inc. [Online]. Available: https://mathworld.wolfram.com/DivisionbyZero.html. [Accessed Jan. 25, 2022]
[4] Wikipedia.org, 2022. IEEE 754 - Wikipedia. Wikimedia Foundation, Inc. [Online]. Available: https://en.wikipedia.org/wiki/IEEE_754. [Accessed Jan. 25, 2022]
[5] Oracle, 2021. The Java® Language Specification. Chapter 15. Expressions. Division Operator /. Oracle Corporation and/or its affiliates. [Online]. Available: https://docs.oracle.com/javase/specs/jls/se17/html/jls-15.html#jls-15.17.2. [Accessed Jan. 25, 2022]
[6] Oracle, 2021. Double (Java SE 17 & JDK 17). Oracle and/or its affiliates. [Online]. Available: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Double.html#POSITIVE_INFINITY. [Accessed Jan. 25, 2022]
[7] Oracle, 2021. BigDecimal (Java SE 17 & JDK 17). Oracle and/or its affiliates. [Online]. Available: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/math/BigDecimal.html. [Accessed Jan. 25, 2022]
[8] Encyclopedia of Mathematics, 2021. Infinite decimal expansion - Encyclopedia of Mathematics. European Mathematical Society. [Online]. Available: http://encyclopediaofmath.org/index.php?title=Infinite_decimal_expansion&oldid=33414. [Accessed Jan. 25, 2022]