1、float整数计算误差
案例:会员积分字段采用float类型,导致计算会员积分时,7位整数的数据计算结果出现误差。
原因:超出float精度范围,无法精确计算。
float和double的精度是由尾数的位数来决定的。浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“1”,由于它是不变的,故不能对精度造成影响。
float:2^23 = 8388608,一共七位,这意味着最多能有7位有效数字,但绝对能保证的为6位,也即float的精度为6~7位有效数字;
double:2^52 = 4503599627370496,一共16位,同理,double的精度为15~16位。
难道只是位数多大的问题,字段类型换成double就可以解决吗?对于本案例是这样,因为都是整数计算,但如果有小数位,就不一定了,见下面案例。
2、double小数转bigdecimal后四舍五入计算有误差
案例:
double g= 12.35;
BigDecimal bigG=new BigDecimal(g).setScale(1, BigDecimal.ROUND_HALF_UP); //期望得到12.4
System.out.println("test G:"+bigG.doubleValue());
test G:12.3
原因:
定义double g= 12.35; 而在计算机中二进制表示可能这是样:定义了一个g=12.34444444444444449,
new BigDecimal(g) g还是12.34444444444444449
new BigDecimal(g).setScale(1, BigDecimal.ROUND_HALF_UP); 得到12.3
正确的定义方式是使用字符串构造函数:
new BigDecimal("12.35").setScale(1, BigDecimal.ROUND_HALF_UP)
3、float和double做四则运算误差
案例:
public class Test{
public static void main(String args[]){
System.out.println(0.05+0.01);
System.out.println(1.0-0.42);
System.out.println(4.015*100);
System.out.println(123.3/100);
}
}
结果:
0.060000000000000005
0.5800000000000001
401.49999999999994
1.2329999999999999
原因:
那么为什么会出现精度丢失呢?在查阅了一些资料以后,我稍微有了一些头绪,下面是本人的愚见,仅供参考。
首先得从计算机本身去讨论这个问题。我们知道,计算机并不能识别除了二进制数据以外的任何数据。无论我们使用何种编程语言,在何种编译环境下工作,都要先
把源程序翻译成二进制的机器码后才能被计算机识别。以上面提到的情况为例,我们源程序里的2.4是十进制的,计算机不能直接识别,要先编译成二进制。但问 题来了,2.4的二进制表示并非是精确的2.4,反而最为接近的二进制表示是2.3999999999999999。原因在于浮点数由两部分组成:指数和尾数,这点如果知道怎样进行浮点数的二进制与十进制转换,应该是不难理解的。如果在这个转换的过程中,浮点数参与了计算,那么转换的过程就会变得不可预
知,并且变得不可逆。我们有理由相信,就是在这个过程中,发生了精度的丢失。而至于为什么有些浮点计算会得到准确的结果,应该也是碰巧那个计算的二进制与 十进制之间能够准确转换。而当输出单个浮点型数据的时候,可以正确输出,如
double d = 2.4;
System.out.println(d);
输出的是2.4,而不是2.3999999999999999。也就是说,不进行浮点计算的时候,在十进制里浮点数能正确显示。这更印证了我以上