Skip to content

Latest commit

 

History

History
69 lines (47 loc) · 3.8 KB

浮点数.md

File metadata and controls

69 lines (47 loc) · 3.8 KB

浮点数计算

最近在项目中遇到问题,console.log(5.1 - 3) ,结果为 2.0999999999999996 ,查阅相关资料了解到,原来是浮点数计算的问题。关于浮点数最突出的例子就是 0.1 + 0.2 = 0.30000000000000004 。我们都知道是因为在转二进制的时候,0.1和0.2不能用二进制精确表示,会丧失精确,但是小数位的二进制怎么计算?精确值又是怎么丢失的呢?

IEEE-754双精度

在JavaScript中,number类型(不区分整数值和浮点数值)都是以 64bit 双精度浮点数存储的,双精度的表现形式如下图:

S(符号位) E(指数位) F(尾数位)
位数 1 11 52
位置 63 62 - 52 51 - 0

浮点数在计算机中的表示是基于科学计数法的,比如 1234 ,用科学计数法表示应该是 1.234 * 10³ ,这里分为3部分,对照上表,第一部分是符号位,1表示负数,0表示正数,第二部分是指数位,用接下来的11位表示,这里的指数有偏移值。因为指数也是可正可负,处理方法有2种,第一种的在指数位也添加一个符号位,第二种方法就是设置一个 偏移 ,使指数部分一直表现为一个非负数,然后减去偏移量才是真正的指数,而64bit设置的偏移量是 1023 。第三部分是尾数位(有效数字),当尾数的值不为0时,尾数的最高有效位应为1,这称为浮点数的规格化,可以节省出一位来用于提高精度,这里的尾数位就是1.234。接下来我们看一下精度是怎么丢失的。

十进制转换二进制以及二进制转换为浮点数

十进制整数转换为二进制整数采用 除2取余,逆序排列 法,这个我们都很清楚了,所以我们主要说一下十进制小数怎么转换为二进制。 小数位转二进制采用 乘2取整,顺序排列 的方法,比如十进制的4.125,我们分为以下几步:

1、转换二进制

4.125 整数部分4转换为二进制是 100 ,小数位 0.125 根据 乘2取整,顺序排列 计算

0.125 * 2 = 0.25 -------- 取整数 0
0.25 * 2 = 0.5   -------- 取整数 0
0.5 * 2 = 1.0    -------- 取整数 1

所以 4.125 转为二进制就是 100.001

2、规范化

从上面我们知道,浮点数的规格化是尾数位最高位为1,那么我们转换一下得到 1.00001 * 2^2

3、转为浮点数

  • 符号位:正数,所以为0
  • 指数位:指数位是2,加上偏移量1023,E = 1025
  • 尾数位:1.00001 取小数部分为 00001

所以 4.125 转为浮点数就是

S(符号位) E(指数位) F(尾数位
0 10000000001 0000100....0000
1位 11位 52位

小数位的精度怎么丢失的

我们用 0.1 + 0.2 来说精度怎么发生的丢失。

0.1转换为二进制

0.1 * 2 = 0.2  ------ 取整数 0
0.2 * 2 = 0.4  ------ 取整数 0
0.4 * 2 = 0.8  ------ 取整数 0
0.8 * 2 = 1.6  ------ 取整数 1
0.6 * 2 = 1.2  ------ 取整数 1
0.2 * 2 = 0.4  ------ 取整数 0
0.4 * 2 = 0.8  ------ 取整数 0
0.8 * 2 = 1.6  ------ 取整数 1
0.6 * 2 = 1.2  ------ 取整数 1
0.2 * 2 = 0.4  ------ 取整数 0

所以0.1转换为二进制就是 0.0001100110011.....00110011 ,我们发现无限循环了,但是浮点数的尾数只能放下52位,那剩下的只能舍弃了,所以这里就丢失了精度。还有当0.1和0.2被存储时,存进去的已经不是精确的0.1和0.2了,而是精度发生一定丢失的值,当这个两个值发生相加时,精度还可能进一步丢失。