摘要
Java语言中支持两类非整数类型,float和double(以及包装类Float和Double)。还有一个特殊的类,任意精度小数BigDecimal。大多数编程情况都很少用到浮点数等的运算,但是在实际应用中,这些非整数运算总是出现错误的计算结果。文章将讨论非整数类型的基本原理,如何使用非整数得到正确的结果,以及BigDecimal的推荐使用。
存储原理
计算机中以底数为2的小数来表示浮点数。
在32位的float类型浮点数中,通常用1位表示数字符号,8位表示指数,其余23位表示小数部分。有符号数据指数部分有正负之分,小数部分由以2为底的小数表示。即最高为对应为(2 -1)。下一位对应(2 -2),以此类推。
对于64位双精度double类型,1位表示数字符号,11位表示指数,其余52位表示小数部分。
s | exponent | mantissa | |
float | 1 | 8 | 23 |
double | 1 | 11 | 52 |
计算误差
float数据311.29,其真实值是311.2900085449219。浮点数的计算机存储方式注定其不能精确的表示所有数据,因此,float和double型数据在计算过程中的误差是在所难免的。
比较错误
1,运算过程中,当比较其值是否大于0时,由于误差的存在,会出现错误的比较结果。
2,对于特殊情况,无穷大NaN,正负零的比较需特别注意。比如0是不等于-0的。
BigDecimal
BigDecimal是在jdk1.3中增加的内容,属于标准类库,可表示任意精度的小数。其通过任意精度任何范围的值和一个换位因子来表示BigDecimal,换位因子表示小数点左移的位数。用BigDecimal表示的值为
unscaledValue*10 -scale
。
BigDecimal有多个构造函数,其中一个构造函数以双精度浮点数作为输入,另一个以整数和换算因子作为输入,还有一个以小数的
String
表示作为输入。小心使用 BigDecimal(double)
构造函数,double在计算存储中存在误差,有可能导致构造完的BigDecimal就存在误差。请使用基于整数或 String
的构造函数。BigDecimal
提供了基本的加、减、乘、除等算数运算,由于其对象的不可变性,在运算的过程中都会产生新的BigDecimal
对象。创建对象的开销,因此在使用BigDecimal时需谨慎,避免用于大量数学计算。
总结
1,使用float和double类型在计算时会存在误差,如果需要非常精确的计算,建议使用BigDecimal。
2,如果数据计算需要进行四色五入到整数,值加0.5的办法处理。
3,对精度要求不高的环境下,比如测量值,允许存在误差且float和double导致的误差在其范围内。float和double数据原型是比较好的选择。