Java 如何处理 BigDecimal 中的舍入
How to deal with rouding in BigDecimal in Java
我有这个代码来获取百分比:
final BigDecimal price = BigDecimal.valueOf(215).setScale(2, BigDecimal.ROUND_HALF_EVEN);
final BigDecimal daysOfThisReservation = BigDecimal.valueOf(3).setScale(2, BigDecimal.ROUND_HALF_EVEN);
final BigDecimal dayPrice = price.divide(daysOfThisReservation, BigDecimal.ROUND_HALF_EVEN).setScale(2, BigDecimal.ROUND_HALF_EVEN);
System.out.println(dayPrice); //71.67
System.out.println(dayPrice.multiply(daysOfThisReservation).setScale(2, BigDecimal.ROUND_HALF_EVEN)); //215.01
我有一个 215.00 美元的预订,这个预订每天有 3 个,
所以,价格是 71.67 美元。
如果我再次尝试获取原始值,我会遇到一些舍入问题,71.67 * 3 = 215.01
遇到这种问题怎么处理?
我们这样看。内存和通信很便宜。
BigDecimal totalPrice = new BigDecimal(double 215.00);
int numNight = 3;
BigDecimal perNightPrice = totalPrice
.divide(new BigDecimal(int numNight), BigDecimal.ROUND_HALF_EVEN)
.setScale(2, BigDecimal.ROUND_HALF_EVEN);
此时,你有三个变量。鉴于内存(主 RAM 和辅助 disk/DB)和通信都很便宜,您可以只保留 totalPrice 变量。您不需要从 perNightPrice 和 numNight 进行逆向工程。
我以前遇到过这种情况。我们只是更改 API and/or 数据 table 定义以允许我们保留更多数据(除非您正在为 Lunar-Lander 中的 memroy 报废——我怀疑是这种情况因为 Lunar-Lander 不计算酒店价格)。
我的感觉是,这里的问题不是丢弃原价,而是通过将小数截断到小数点后两位来丢弃有价值的信息。如果你多保留一位小数,你就不会有这个问题了。
这里有一个示例,它保持 6 位精度,以允许足够的精度对值进行数百次操作,而我最终只需要两位有效的小数位。如果你最后只关心 2 位,你永远不会有 6 位小数的舍入问题。
除了打印单日价格时,此代码保留 6 位,此时四舍五入为 2 位。
final BigDecimal price = BigDecimal.valueOf(215).setScale(6, BigDecimal.ROUND_HALF_EVEN);
final BigDecimal daysOfThisReservation = BigDecimal.valueOf(3).setScale(6, BigDecimal.ROUND_HALF_EVEN);
final BigDecimal dayPrice = price.divide(daysOfThisReservation, BigDecimal.ROUND_HALF_EVEN).setScale(6, BigDecimal.ROUND_HALF_EVEN);
System.out.println(dayPrice.setScale(2, BigDecimal.ROUND_HALF_EVEN));
System.out.println(dayPrice.multiply(daysOfThisReservation).setScale(2, BigDecimal.ROUND_HALF_EVEN)); //215.01
结果:
71.67
215.00
可以提高到一定的精度吗?
final BigDecimal price = BigDecimal.valueOf(215).setScale(10, BigDecimal.ROUND_HALF_EVEN);
final BigDecimal daysOfThisReservation = BigDecimal.valueOf(3).setScale(10, BigDecimal.ROUND_HALF_EVEN);
我有这个代码来获取百分比:
final BigDecimal price = BigDecimal.valueOf(215).setScale(2, BigDecimal.ROUND_HALF_EVEN);
final BigDecimal daysOfThisReservation = BigDecimal.valueOf(3).setScale(2, BigDecimal.ROUND_HALF_EVEN);
final BigDecimal dayPrice = price.divide(daysOfThisReservation, BigDecimal.ROUND_HALF_EVEN).setScale(2, BigDecimal.ROUND_HALF_EVEN);
System.out.println(dayPrice); //71.67
System.out.println(dayPrice.multiply(daysOfThisReservation).setScale(2, BigDecimal.ROUND_HALF_EVEN)); //215.01
我有一个 215.00 美元的预订,这个预订每天有 3 个, 所以,价格是 71.67 美元。
如果我再次尝试获取原始值,我会遇到一些舍入问题,71.67 * 3 = 215.01
遇到这种问题怎么处理?
我们这样看。内存和通信很便宜。
BigDecimal totalPrice = new BigDecimal(double 215.00);
int numNight = 3;
BigDecimal perNightPrice = totalPrice
.divide(new BigDecimal(int numNight), BigDecimal.ROUND_HALF_EVEN)
.setScale(2, BigDecimal.ROUND_HALF_EVEN);
此时,你有三个变量。鉴于内存(主 RAM 和辅助 disk/DB)和通信都很便宜,您可以只保留 totalPrice 变量。您不需要从 perNightPrice 和 numNight 进行逆向工程。
我以前遇到过这种情况。我们只是更改 API and/or 数据 table 定义以允许我们保留更多数据(除非您正在为 Lunar-Lander 中的 memroy 报废——我怀疑是这种情况因为 Lunar-Lander 不计算酒店价格)。
我的感觉是,这里的问题不是丢弃原价,而是通过将小数截断到小数点后两位来丢弃有价值的信息。如果你多保留一位小数,你就不会有这个问题了。
这里有一个示例,它保持 6 位精度,以允许足够的精度对值进行数百次操作,而我最终只需要两位有效的小数位。如果你最后只关心 2 位,你永远不会有 6 位小数的舍入问题。
除了打印单日价格时,此代码保留 6 位,此时四舍五入为 2 位。
final BigDecimal price = BigDecimal.valueOf(215).setScale(6, BigDecimal.ROUND_HALF_EVEN);
final BigDecimal daysOfThisReservation = BigDecimal.valueOf(3).setScale(6, BigDecimal.ROUND_HALF_EVEN);
final BigDecimal dayPrice = price.divide(daysOfThisReservation, BigDecimal.ROUND_HALF_EVEN).setScale(6, BigDecimal.ROUND_HALF_EVEN);
System.out.println(dayPrice.setScale(2, BigDecimal.ROUND_HALF_EVEN));
System.out.println(dayPrice.multiply(daysOfThisReservation).setScale(2, BigDecimal.ROUND_HALF_EVEN)); //215.01
结果:
71.67
215.00
可以提高到一定的精度吗?
final BigDecimal price = BigDecimal.valueOf(215).setScale(10, BigDecimal.ROUND_HALF_EVEN);
final BigDecimal daysOfThisReservation = BigDecimal.valueOf(3).setScale(10, BigDecimal.ROUND_HALF_EVEN);