原文地址:https://alphahinex.github.io/2021/04/04/binary-operations/
coverdescription: "逢二进一,小数存在精度问题"
date: 2021.04.04 10:34
categories:
- Others
tags: [Java, JavaScript]
keywords: 十进制, 二进制, 浮点数, float, double, ieee 754, binary
总体来讲,二进制算术运算可以采用竖式计算的方式,与十进制竖式计算的区别是,逢十进一变为了逢二进一。
整数
以 8 和 4 为例,可参照 十进制整数与二进制数转换 中方法,分别计算出二进制形式:
8 (10) = 1000 (2)
4 (10) = 0100 (2)
加
1000
+ 0100
------
1100
1100 (2) = 12 (10)
减
1000
- 0100
------
0100
0100 (2) = 4 (10)
负数怎么表示
如果是 4 - 8 时会怎么样呢?
0100
- 1000
------
1100
因为第一位 0 - 1 时始终需要向前一位借一,故无论使用几位二进制数表示此负数,前面都会补 1
,如 1111 1100
。
表示有符号的数时,二进制数的第一位用来表示符号位,0 代表正数,1 代表负数。
在计算机中,使用 补码
来表示负数。补码
为 反码+ 1
,而 反码
的意思是按位取反。
通过补码的逆运算(先减一,再取反),我们可以算出,1111 1100
即为 -4
:
1111 1100 - 1
= 1111 1011
再取反
=> 0000 0100
= 1 * 2^2 = 4
乘
1000
x 0100
------
0000
0000
1000
------
100000
100000 (2) = 32 (10)
除
0010
_______
0100 ) 1000
100
------
0
0010 (2) = 2 (10)
浮点数
浮点数运算时,可先参照 十进制浮点数与二进制数转换 中方式,将浮点数转换为二进制形式,未转换为 IEEE 754 形式时,可参照上述方式直接运算,如:
0.1
+ 0.01
------
0.11
0.1 (2) = 0.5 (10)
0.01 (2) = 0.25 (10)
0.11 (2) = 0.75 (10)
由浮点数转换二进制的方式可以知道,只有 m*2^-n(m、n 为整数),才能转换成有限位数的二进制数(如 0.5,0.25,0.125 ……);其余的小数表示成二进制时,都会因精度限制,仅能表示为近似值。
如果浮点数转换为了 IEEE 754 标准的形式,在计算时,需要注意两个二进制数的指数部分是否相同。
如果指数相同,同样可以按照上述方式,直接将小数部分进行运算。
如果指数不同,则需要先将指数调整为相同的情况,再进行计算。
以经典的 0.1 + 0.2 为例:
0.1 (10) = 0.0001 1001 1001 1001 ... (2)
0.2 (10) = 0.0011 0011 0011 0011 ... (2)
使用 IEEE 754 双精度浮点数方式表示时:
0.1 (10) => 0 01111111011 1001100110011001100110011001100110011001100110011010
0.2 (10) => 0 01111111100 1001100110011001100110011001100110011001100110011010
注意,因为精度限制,需截断小数位 52 位之后的
1001 ……
,故小数位最后的1001
发生进位,变成了1010
。
对比可以发现,0.1 和 0.2 的双精度浮点数二进制表示中,仅指数位部分不同。
此时,在进行加法运算时,需先将指数部分转换为相同的值。
0.1 的指数部分为 01111111011 (2) = 1019 (10) = 1023 - 4
,即指数为 2^(-4)
;
0.2 的指数部分为 01111111100 (2) = 1020 (10) = 1023 - 3
,即指数位 2^(-3)
。
双精度指数偏移量为
2^10-1=1023
。
可将 0.1 的小数部分左移一位,使其与 0.2 的指数部分相同。因二进制科学计数法表示形式为 1.xxxx * 2^n
,故左移后的 0.1 的小数部分变为
1.1001100110011001100110011001100110011001100110011010 * 2^(-4)
=>
0.11001100110011001100110011001100110011001100110011010 * 2^(-3)
保留 52 位,末位 0 可直接省略,故 0.1 + 0.2 可以按如下形式进行计算:
0.1100110011001100110011001100110011001100110011001101 * 2^(-3)
+ 1.1001100110011001100110011001100110011001100110011010 * 2^(-3)
---------------------------------------------------------------------
10.0110011001100110011001100110011001100110011001100111 * 2^(-3)
转换为科学计数法,可表示为:
10.0110011001100110011001100110011001100110011001100111 * 2^(-3)
=>
1.00110011001100110011001100110011001100110011001100111 * 2^(-2)
因双精度只能保留 52 位小数,舍弃末位 1 并进位,小数部分变为:
1.00110011001100110011001100110011001100110011001100111 * 2^(-2)
=>
1.0011001100110011001100110011001100110011001100110100 * 2^(-2)
小数部分左移一位,相当于指数部分加一,指数部分变为:
01111111100
=>
01111111101
故 0.1 + 0.2 最终的双精度二进制浮点数表示为:
0 01111111101 0011001100110011001100110011001100110011001100110100
转换为十进制:
2^-2+2^-5+2^-6+2^-9+2^-10+2^-13+2^-14+2^-17+2^-18+2^-21+2^-22+2^-25+2^-26+2^-29+2^-30+2^-33+2^-34+2^-37+2^-38+2^-41+2^-42+2^-45+2^-46+2^-49+2^-50+2^-52
=
0.3000000000000000444089209850062616169452667236328125
以上结果可通过 在线精度计算器 计算获得。
网友评论