在项目中用Java作浮点数计算时,发现对于4.015*100这样的计算,结果不是预料中的401.5,而是401.49999999999994。如此长的位数,对于显示来讲很不友好。java
查阅相关资料,发现缘由是:计算机中的浮点数并不能彻底精确表示。例如,对于一个double型的38414.4来讲,计算机是这样存储它的:数组
double | 符号位1位 | 阶码11位 | 尾数52位 |
从这里能够看出来,主要缘由在于二进制编码使得小数部分没法彻底精确表示,例如0.4 = 0.25 + 0.125 + ...,只能无限接近。因此在对浮点数作计算时会产生精度偏差。性能
Java中的BigDecimal能够支持任意精度的浮点数运算。在《Effective Java 》这本书中建议:float 和double 用来作科学计算或者是工程计算,而在商业计算中使用java.math.BigDecimal 。编码
BigDecimal有多种构造方法,如BigDecimal(double),BigDecimal(String),须要注意的是:构造参数为String类型时才能保证不丢失精度,由于double类型自己就是不彻底精确的。故须要写成这样:BigDecimal("0.02")。spa
double类型的基本运算都能在BigDecimal中找到相对应的方法。另外,BigDecimal还能够配合NumberFormat作格式化输出。orm
BigDecimal在作运算的时候都会生成新的BigDecimal对象,所以相对double来讲会带来更多的性能开销。对象
那么BigDecimal是如何作到可以表示任意精度的呢?这里只作一个初步的分析。ci
首先看BigInteger的实现。普通的int型是32位,所以有范围限制。BigInteger中有成员变量int[] mag,这样变长的int数组使得表示任意大小的整数成为可能。源码
再看BigDecimal的实现。它的官方介绍中说,任意一个BigDecimal均可以表示为unscaledValue × 10^-scale的形式。unscaledValue是一个任意大小的整数,在源代码中对应BigInteger intVal这个成员变量;scale是阶数,在源代码中对应int scale这个变量。这样就在BigInteger的基础上获得了BigDecimal的实现。table
更细节的内容能够自行阅读源码作进一步分析。