Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

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.0

pow, 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
true

BigInteger

Для целых чисел, которые не помещаются даже в 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.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.