Math and BigDecimal
For calculations Java has the Math class with ready-made mathematical functions. And when every cent matters, you use the BigDecimal class instead of double — it computes decimal fractions exactly.
The Math class
Math methods are called through the class name; there is no need to create an object. max() and min() pick the larger and the smaller value, abs() gives the absolute value, pow(a, b) raises to a power, sqrt() takes the square root. There are three rounding methods: round() — to the nearest whole number, floor() — down, ceil() — up:
void main() {
IO.println(Math.max(7, 12));
IO.println(Math.abs(-5));
IO.println(Math.pow(2, 10));
IO.println(Math.sqrt(16));
IO.println(Math.round(2.5));
IO.println(Math.floor(2.7));
IO.println(Math.ceil(2.1));
}Output:
12
5
1024.0
4.0
3
2.0
3.0pow, sqrt, floor and ceil return a double, while round returns a whole long. There are constants too: Math.PI is the number π.
The imprecision of double
A double stores a number in binary, and many decimal fractions, such as 0.1, are infinite in binary. So only an approximation is stored, and a tiny error shows up in calculations:
void main() {
IO.println(0.1 + 0.2);
IO.println(0.1 + 0.2 == 0.3);
}Output:
0.30000000000000004
falseFor physics or graphics such an error does not matter, but for money it is unacceptable.
BigDecimal
The BigDecimal class stores a decimal number exactly. Create it from a string and calculate with the add(), subtract() and multiply() methods — the usual + and * do not work with objects:
void main() {
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
IO.println(a.add(b));
BigDecimal price = new BigDecimal("19.99");
IO.println(price.multiply(new BigDecimal("3")));
}Output:
0.3
59.97Rounding to cents
setScale() sets the number of digits after the decimal point, and RoundingMode sets the rounding rule. HALF_UP is the one taught at school: five and above rounds up. Division also needs a precision, otherwise for 10 / 3 Java throws an exception — the result is infinite:
void main() {
BigDecimal price = new BigDecimal("19.99");
BigDecimal tax = price.multiply(new BigDecimal("0.2"));
IO.println(tax);
IO.println(tax.setScale(2, RoundingMode.HALF_UP));
BigDecimal share = new BigDecimal("10").divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP);
IO.println(share);
}Output:
3.998
4.00
3.33Comparing BigDecimal
The equals() method of BigDecimal also takes the number of decimal places into account: 2.0 and 2.00 are different for it. To compare just the values, use compareTo() — it returns 0 when the numbers are equal:
void main() {
BigDecimal a = new BigDecimal("2.0");
BigDecimal b = new BigDecimal("2.00");
IO.println(a.equals(b));
IO.println(a.compareTo(b) == 0);
}Output:
false
trueBigInteger
For whole numbers that do not fit even into a long, there is BigInteger. It works the same way — through methods:
void main() {
BigInteger big = BigInteger.TWO.pow(100);
IO.println(big);
}Output:
1267650600228229401496703205376Common mistake
If you create a BigDecimal from a double, it faithfully keeps the already imprecise binary value — error and all:
void main() {
BigDecimal wrong = new BigDecimal(0.1);
IO.println(wrong);
}Output:
0.1000000000000000055511151231257827021181583404541015625Pass a string: new BigDecimal("0.1"). If the number is already in a double, use BigDecimal.valueOf(0.1) — it gives 0.1.