计算机浮点数运算存在误差的原因

计算机中的二进制的小数同十进制一样,比如十进制里面10.125可以写成1.125*10e1(10e1=10^{1}:即10的1次方),二进制里面1010.001可以写成1.010001*2e3,都可以写成谁乘以进制的指数次,所以,在IEEE规则中规定浮点数的构成是符号位S+指数位E+尾数M,这个位数的组合构成浮点数

JAVA遵守IEEE754规则:

float占4个字节,32位,表示为SEEEEEEEEMMMMMMMMMMMMMMMMMM,1位符号位,8位指数位,23位尾数位。当然如果指数值在1-254之间时,为1.M,否则就是0.M,指数又叫阶码,阶码采用移码表示,取值E-127.所以公式可总结为:小于0的小数:(-1)的S次*2的(1-127)次*(0.M),大于0的小数:(-1)的S次*2的(E-127)*(1.M).

5.0的在JAVA中的计算机二进制流表示为:

S=0,
5.0=101=1.01*2的2次
E-127=2  E=129=10000001
M=1.01

组合在一起就是 0 10000001 01000000000000000000000

再转成十进制:(-1)的0次 * 2的(129-127)次*(1.01)=101=5.0

输出float的二进制流,Java中的方法Float.floatToIntBits()

-5.0的表示:

Java二进制流:1 10000001 01000000000000000000000,其他省略。

0.1的表示:

0.1约等于0.0001100110011001100110011001=1.100110011001100110011001*2的-4次

S=0

E-127=-4 E=123=01111011

M=10011001100110011001100

组合在一起在JAVA中二进制流就是0 01111011 100110011001100110011001100

在转换成10进制为0.100000001490116119384765625

误差为0.000000001490116119384765625 如果26个float的0.1相加,结果为2.5999997 如果定义26个double的0.1相加,结果为2.600000000000001。

所以可以得出:

1.浮点数强制转换成整数时,舍入误差严重加重,强制转换时会舍弃非整数部分。

double d=29*0.01;//d=0.29
d=((int)d*100);//d=28

2.科学计数表示法中二进制位数越多,精度就越高。64位double就比32位的float精度高。对精度要求非常高的场合可以用BigDecimal类,能表示任意精度的数。

3.指数E为255时表示特殊数字。

--------------------这里是传说中的分割线--------------------

引用另外一篇文章来说明,以帮助理解:

浮点数是用来表示实数的一种方法,它用M(尾数) * B(基数)的E(指数)次方来表示实数,相对于定点数来说,在长度一定的情况下,具有表示数据范围大的特点。但同时也存在误差问题,这就是著名的浮点数精度问题。

浮点数有多种实现方法,计算机中浮点数的实现大都遵从IEEE754标准,IEEE754规定了单精度浮点数和双精度浮点数两种规格。

单精度浮点数用4字节(32bit)表示浮点数,格式是:

1位符号位 8位表示指数 23位表示尾数

双精度浮点数8字节(64bit)表示实数,格式是:

1位符号位 11位表示指数 52位表示尾数

同时,IEEE754标准还对尾数的格式做了规范:d.dddddd...,小数点左面只有1位且不能为零,计算机内部是二进制,因此,尾数小数点左面部分总是1。显然,这个1可以省去,以提高尾数的精度。

由上可知,单精度浮点数的尾数是用24bit表示的,双精度浮点数的尾数是用53bit表示的,转换成十进制:

2^24 - 1 = 16777215; 2^53 - 1 = 9007199254740991

由上可见,IEEE754单精度浮点数的有效数字二进制是24位,按十进制来说,是8位;双精度浮点数的有效数字二进制是53位,按十进制来说,是16位。显然,如果一个实数的有效数字超过8位,用单精度浮点数来表示的话,就会产生误差!同样,如果一个实数的有效数字超过16位,用双精度浮点数来表示,也会产生误差!对于1310720000000000000000.66这个数,有效数字是24位,用单精度或双精度浮点数表示都会产生误差,只是程度不同:

单精度浮点数:1310720040000000000000.00;双精度浮点数: 1310720000000000000000.00

可见,双精度差了 0.66 ,单精度差了近4万亿!

以上说明了因长度限制而造成的误差,但这还不是全部,采用IEEE754标准的计算机浮点数,在内部是用二进制表示的,但在将一个十进制数转换为二进制浮点数时,也会造成误差,原因是不是所有的数都能转换成有限长度的二进制数。

对于131072.32 这个数,其有效数字是8位,按理应该能用单精度浮点数准确表示,为什么会出现偏差呢?

看一下这个数据二进制尾数就明白了 10000000000000000001010001......

显然,其尾数超过了24bit,根据舍入规则,尾数只取 100000000000000000010100,结果就造成测试中遇到的“奇怪”现象,131072.68用单精度浮点数表示变成131072.69,原因与此类似。

实际上有效数字小于8位的数,浮点数也不一定能精确表示,7.22这个数的尾数就无法用24bit二进制表示,当然在数据库中测试不会有问题(舍入以后还是7.22),但如果参与一些计算,误差积累后,就可能产生较大的偏差。

您的赞助将会支持作者创作及本站运维

发表评论


TOP