这是一个很常见的问题,在我们用 float 或 double 进行数值计算时,经常会有保留小数多少位数的需求,而不管我们是用强制转换,还是用封装类 Float/Double 自带的转换方法,精度丢失永远是一个跨不过去的坎。出现精度丢失的根本原因是:数学意义上的小数不是每个都能用二进制在有限位数内精确的表示。(像 0.1、1.1 这样的小数并没有精确的二进制表示,所以求和后也就不是精确的 1.2 了)
如果需要精确计算的话就用字符串来保存和计算,各种语言标准库都会提供类似 Decimal 的类,封装好了类似操作。
对 Java 来说,就是用 BigDecimal 类了。不过要注意,使用 BigDecimal(double val) 构造函数时仍会存在精度丢失问题,建议使用 BigDecimal(String val)。这就需要先把 double 转换为字符串然后在作为 BigDecimal(String val) 构造函数的参数。转换为 BigDecimal 对象之后再进行加减乘除操作,这样精度就不会出现问题了。
在《Effective Java》这本书中,也提到了这个原则:float 和 double 只能用来做科学计算或者是工程计算,在商业计算中我们要用 BigDecimal,并且一定要用 String 来够造 BigDecimal。这也是为什么有关金钱数据存储的都使用 BigDecimal。
1 | System.out.println(0.11 + 2001299.32); |
下面是常用的运算方法,可放在工具类中使用:
1 | /** |
(完)