java中double运算的精度丢失问题

来源:互联网 发布:ajax跨域请求不是json 编辑:程序博客网 时间:2024/05/21 00:17

在java中,有double a=0.03,b=0.02,c=a-b;c的取值应该是0.01,而实际值确实0.009999999999999998。

说明:在java中双精度浮点数double是8个字节,即64bit。在IEEE 754标准中规定64位浮点数格式中,符号位s为1位,阶码e为11位,尾数f为52位。尾数用原码表示,规格化尾数第一位总是1,因此可在尾数中缺省第一位的1,该缺省位叫隐藏位。阶码用移码形式,偏置常数2^n-1。计算出在双精度浮点数中偏置常数是1023.

0.03= 0.0000011110101110000101000111101011100001010001111010111B=

1.1110101110000101000111101011100001010001111010111)*2^-6=(-1)^s*1.f*2^(e-1023),

所以s=0,f=0.1110101110000101000111101011100001010001111010111,e=1017=01111111001B.得出a的双精度浮点数格式0 0111 1111 001 1110 1011 1000 0101 0001 1110 1011 1000 0101 0001 1110 1011 1000 0

同样的,对于b

0.02=

 0.0000010100011110101110000101000111101011100001010001111011B=

1.0100011110101110000101000111101011100001010001111011)*2^-6=(-1)^s*1.f*2^(e-1023),所以s=0,f=0.0100011110101110000101000111101011100001010001111011,e=1017=01111111001B.得出a的双精度浮点数格式0 0111 1111 001 0100 0111 1010 1110 0001 0100 0111 1010 1110 0001 0100 0111 1011 0

c=a-b,

浮点数运算步骤

1)对阶 

对阶原则:小阶向大阶看齐,阶小的数尾数右移,右移的位数等于两个阶的差的绝对值。通用计算机多采用IEEE 754标准来表示浮点数,因此阶小的数右移时按原码小数方式右移,符号位不参加移位,数值位要将隐藏的1右移到小数部分,前面空出的位补0,且为保证运算的精度,尾数右移时,低位移除的位不要丢弃,应该保留并参加尾数部分的运算。

2)尾数加减

定点原码小数加减运算,在进行尾数加减时,必须把隐藏位还原到尾数部分,对阶过程中尾数右移时保留的附加位也要参加运算。

3)尾数格式化

右规或左规

4)尾数舍入


进过浮点数的运算之后,c的双精度浮点数格式应该是0 1111111000 010001111010111000010100011110101110000101000111101=

 0.0099999999999999982236431605997495353221893310546875。
解决这个问题的方法是使用java.math.BigBecimal。