Почему для хранения денег не подходят примитивные типы, например double
Что проверяют
Понимаете ли вы, что double это двоичная дробь по стандарту IEEE 754, а не «число с запятой», и знаете ли, чем за подмену платят. По ответу видно, работали ли вы с деньгами всерьёз или только читали про округление.
Как отвечать
Скажите главное: float и double хранят значение в двоичной системе, а десятичные дроби вроде 0.1 в неё точно не переводятся. Число в памяти уже не то, которое вы написали в коде.
System.out.println(0.1 + 0.2); // 0.30000000000000004
System.out.println(2.00 - 1.10); // 0.8999999999999999На одной операции погрешность незаметна, но проценты, налоги и суммирование по тысячам строк её накапливают, и итог в отчёте расходится с итогом в базе на копейки. Для денег это дефект, а не мелочь.
Дальше назовите два рабочих варианта:
BigDecimal: десятичная арифметика с явными масштабом и правилом округления,setScale(2, RoundingMode.HALF_UP). Именно его ждут как ответ по умолчанию;- целое в минорных единицах: копейки в
long. Быстрее и проще, подходит там, где не нужно делить и считать доли процента.
Добавьте, что валюта это часть суммы: денежный тип хранит и число, и код валюты, иначе рубли однажды сложатся с долларами.
Где спотыкаются
Берут new BigDecimal(0.1) и получают длинный хвост. Конструктор от double принимает уже испорченное значение, создавать нужно из строки: new BigDecimal("0.1") или BigDecimal.valueOf(0.1).
Второе: сравнивают через equals. Он учитывает масштаб, поэтому 2.0 и 2.00 для него не равны. Для сравнения по значению есть compareTo.
Третье: забывают про деление. BigDecimal не округляет сам, и деление с бесконечной десятичной дробью без указанных масштаба и режима округления просто падает с ArithmeticException.