Math и BigDecimal
Для вычислений в Java есть класс Math с готовыми математическими функциями. А когда важна каждая копейка, вместо double берут класс BigDecimal — он считает дробные числа точно.
Класс Math
Методы Math вызываются через имя класса, объект создавать не нужно. max() и min() выбирают большее и меньшее, abs() даёт модуль, pow(a, b) возводит в степень, sqrt() извлекает квадратный корень. Для округления есть три метода: round() — к ближайшему целому, floor() — вниз, ceil() — вверх:
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));
}Результат:
12
5
1024.0
4.0
3
2.0
3.0pow, sqrt, floor и ceil возвращают double, а round — целое long. Есть и константы: Math.PI — число π.
Неточность double
Тип double хранит число в двоичном виде, а многие десятичные дроби, например 0.1, в двоичной системе бесконечны. Поэтому сохраняется лишь приближение, и в вычислениях появляется крошечная погрешность:
void main() {
IO.println(0.1 + 0.2);
IO.println(0.1 + 0.2 == 0.3);
}Результат:
0.30000000000000004
falseДля физики или графики такая ошибка не важна, а для денег — недопустима.
BigDecimal
Класс BigDecimal хранит десятичное число точно. Создавайте его из строки, а считайте методами add(), subtract() и multiply() — обычные + и * с объектами не работают:
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")));
}Результат:
0.3
59.97Округление до копеек
Метод setScale() задаёт число знаков после запятой, а RoundingMode — правило округления. HALF_UP — привычное школьное: пять и больше округляется вверх. Деление тоже требует указать точность, иначе для 10 / 3 Java выбросит исключение — результат бесконечен:
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);
}Результат:
3.998
4.00
3.33Сравнение BigDecimal
Метод equals() у BigDecimal учитывает и число знаков после запятой: 2.0 и 2.00 для него разные. Чтобы сравнить именно значения, используйте compareTo() — он возвращает 0, если числа равны:
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);
}Результат:
false
trueBigInteger
Для целых чисел, которые не помещаются даже в long, есть BigInteger. Он работает так же — через методы:
void main() {
BigInteger big = BigInteger.TWO.pow(100);
IO.println(big);
}Результат:
1267650600228229401496703205376Частая ошибка
Если создать BigDecimal из double, он точно сохранит уже неточное двоичное значение — вместе со всей погрешностью:
void main() {
BigDecimal wrong = new BigDecimal(0.1);
IO.println(wrong);
}Результат:
0.1000000000000000055511151231257827021181583404541015625Передавайте строку: new BigDecimal("0.1"). Если число уже лежит в double, используйте BigDecimal.valueOf(0.1) — он даст 0.1.