Можно ли использовать float и double для точных расчетов и когда они подходят
Что проверяют
Знаете ли вы, почему 0.1 + 0.2 в Java печатает 0.30000000000000004, и умеете ли выбрать тип под задачу. Следом обычно спрашивают, как правильно работать с BigDecimal и как хранить деньги в базе.
Как отвечать
float и double это двоичные числа с плавающей точкой по IEEE 754. У float 32 бита и около 7 значащих десятичных цифр, у double 64 бита и не меньше 15. Большинство десятичных дробей, включая 0.1, в двоичной системе точно не представимы, поэтому ошибка округления есть уже в самом значении, а на тысячах операций она накапливается.
Где они подходят: физика, графика, статистика, машинное обучение, данные с датчиков. Там важна относительная погрешность, а скорость аппаратной арифметики решает. float берут, когда нужно сэкономить память в больших массивах или этого требует графический API.
Для денег и всего, что должно сходиться до копейки:
BigDecimal: десятичная арифметика с явным масштабом и режимом округления, напримерRoundingMode.HALF_UPилиHALF_EVEN. Создавать черезnew BigDecimal("0.1")илиBigDecimal.valueOf(0.1);longв минимальных единицах: сумма в копейках, быстро и без аллокаций, если нет дробных ставок;- в базе:
NUMERICилиDECIMALс фиксированным масштабом, в JPA это полеBigDecimalсprecisionиscaleв@Column.
Сами double сравнивайте с допуском, Math.abs(a - b) < 1e-9, а не через ==.
Где спотыкаются
Пишут new BigDecimal(0.1). Конструктор получает уже неточный double, и внутри оказывается 0.1000000000000000055511151231257827021181583404541015625. Нужна строка или valueOf.
Сравнивают BigDecimal через equals. Он учитывает масштаб, поэтому 2.0 и 2.00 не равны. Для сравнения значений есть compareTo.
Делят без масштаба: BigDecimal.ONE.divide(BigDecimal.valueOf(3)) бросает ArithmeticException, потому что результат бесконечная дробь. Нужно передать масштаб с RoundingMode или MathContext.