Если использовать типы double или float в Java, то можно заметить, что некоторые десятичные дроби не могут быть записаны в них без погрешности. Это связано с особенностью хранения данных в формате с плавающей точкой. Для того, чтобы увидеть эту погрешность, посмотрите вот этот пример:
Погрешность представления чисел в формате с плавающей точкойJava
System.out.println(0.1+0.2);
В консоли будет написано далеко не 0.3.
В некоторых случаях такая погрешность может стать критической. Например, в случае когда мы считаем сумму всех денежных поступлений или списаний.
В Java существует специальный тип для точного хранения десятичных дробей с произвольным количеством знаков после запятой. Это java.math.BigDecimal. Предыдущий пример с использованием BigDecimal будет выглядеть так:
System.out.println(new BigDecimal("0.1").add(new BigDecimal("0.2")));
Теперь мы увидим ровно 0.3, как ожидалось.
Всегда используйте для хранения и обработки денежных сумм java.math.BigDecimal!
Советую также ознакомиться со статьей про числа в Java, где описаны и другие классы.
В java.math.BigDecimal есть другие полезные методы, кроме сложения:
BigDecimal abs() — возвращает абсолютное значение (убирает минус, если он есть).
BigDecimal add(BigDecimal augend) — сложение, которое использовалось в примере.
int compareTo(BigDecimal val) — сравнение. Возвращает 1, если текущее значение больше val, 0 если текущее значение равно val, -1 если текущее значение меньше val.
BigDecimal divide(BigDecimal divisor, int roundingMode) — деление. Количество знаков после запятой у возвращаемого значения this.scale().
BigDecimal divide(BigDecimal divisor, int scale, Rounding roundingMode) — деление. Возвращает BigDecimal, значение которого равно this/divisor, количество знаков после запятой равно scale.
BigDecimal multiply(BigDecimal multiplicand) — умножение. Возвращает результат умножения.
Это далеко не полный список методов. Все методы можно прочитать в официальной документации по ссылке в конце поста.
Обратите внимание, что некоторые методы принимают в качестве параметра RoundingMode. Это способ округления. Округление лучше всего ставить в RoundingMode.HALF_UP, потому что именно так и учат обычно округлять в школе. Старайтесь не использовать метод деления без параметра округления, так как он будет вызывать исключение в случае, когда результат невозможно точно представить в виде конечной десятичной дроби.
Также обратите внимание, что не стоит сравнивать BigDecimal с помощью equals. Используйте для этого метод compareTo, так как он корректно обрабатывает разную точность представления BigDecimal. Метод equals вернёт true только в том случае, если равны их значения и их точность. То есть для equals числа 2.0 и 2.00 не равны.