IEEE 754 Standard

349 阅读3分钟

表示与解析

表示

按照从左至右的顺序:第1位是符号位(sign),接下来的11位是指数位(exponent),剩余的52位是尾数位(fraction)。

IEEE754double-precision-representation-in-memory.png

解析

相当于二进制的科学计数法:

(1)sign(1.b51b50...b0)2×2e1023(-1)^{sign}(1.b_{51}b_{50}...b_0)_2 \times 2^{e-1023}

or

(1)sign(1+i=152b52i)×2e1023(-1)^{sign}(1 + \sum_{i=1}^{52}b_{52-i}) \times 2^{e-1023}

当e为1时,fraction前面的1会变成0:

(1)sign(0.b51b50...b0)2×21022(-1)^{sign}(0.b_{51}b_{50}...b_0)_2 \times 2^{-1022}

exponent的特殊值(处理上、下溢出):

·00000000000 无视fraction的值,整体结果为0
·11111111111 无视fraction的值(全为1除外),整体结果为无穷

举例说明

MAX_VALUE

如何让这个64位二进制数表示一个最大的数?

·表示的数是正数
·其余所有位都是1

然而之前提到过,指数位全为1是一个特殊值,用来表示所有上溢出的数。

因此,最大的数是:

01111111111011111111...1(52bits)0\quad11111111110\quad11111111...1(52bits)

代入公式:

(1)0×1.111...1(52bits)×221121023(-1)^0 \times 1.111...1(52bits) \times 2^{2^{11} - 2 - 1023}

化简得:

1111...1(53bits)×29711111...1(53bits) \times 2^{971}

即:

(2531)×2971(2^{53} - 1) \times 2^{971}

可以在浏览器中通过“Number.MAX_VALUE === (Math.pow(2, 53) - 1) * Math.pow(2, 971)”进行验证。

MIN_VALUE

如何让这个64位二进制数表示一个无限趋近于0的数?

·表示的数是正数
·让exponent和fraction的最低位是1,其余位是0

因此,最小的数是:

0000000000010000...1(52bits)0\quad00000000001\quad0000...1(52 bits)

代入公式:

(1)0×0.0000...1(52bits)×211023(-1)^0 \times 0.0000...1(52\,bits) \times 2^{1-1023}

化简得:

0.0000...1(52bits)×210220.0000...1(52\,bits) \times 2^{-1022}

即:

210742^{-1074}

可以在浏览器中通过“Number.MIN_VALUE === Math.pow(2, -1074)”进行验证。

MAX_SAFE_INTEGER

如何让这个64位二进制数表示一个最大的、且安全的整数?

·表示的数是正数
·fraction全是1
·exponent的结果恰好是52,即e为1075

因此,最大的安全整数是:

0100001101001111...1(52bits)0\quad10000110100\quad1111...1(52 bits)

代入公式:

(1)0×1.1111...1(52bits)×210751023(-1)^0 \times 1.1111...1(52\,bits) \times 2^{1075-1023}

化简得:

1111...1(53bits)1111...1(53\,bits)

即:

25312^{53}-1

可以在浏览器中通过“Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1”进行验证。

MIN_SAFE_INTEGER

MIN_SAFE_INTEGER是MAX_SAFE_INTEGER的相反数,只需将符号位改成1即可。

补充

NaN

当exponent和fraction全为1时,该数为NaN。

为何要在exponent上留出一位来表示所有上下溢出的数,而不是在fraction上留一位

在数值计算中,11111111111、00000000000是进位和退位的两个“尽头”,只要进位、退位后使exponent为11111111111、00000000000,就意味着它的计算结果已经到头了,无视它的fraction部分,直接把它当成无穷或者0。

而如果在fraction上留一位,无法达到这种效果,exponent的每个取值,fraction都有全是1和全是0的情况。就算规定一些条件达成同样的效果,也无法区分无穷和NaN。

为何exponent要用移码表示,且偏移量为1023

fraction为52位,如果不用移码,exponent最小是1,小数最多只能达到小数点后52位,这是二进制的,转化成十进制会更少一些,这样表示的数是不够小的。而使用移码,最小能表示小数点后1074位的数,转化成十进制为5×103245 \times 10^{-324}

至于偏移量为什么是1023,这是因为2112=20462^{1 1 }-2=2046,正负各分一半。

为何0.1 + 0.2 !== 0.3

首先声明,它们是否相等与它们是否被精确表示无关。例如0.1和0.2没有被精确表示,但是0.1 + 0.1 === 0.2。

十进制的浮点数在计算机底层是由二进制的数表示的,这意味着只有形如2n(nZ)2^{-n}(n\in Z)的数,和由多个形如2n(nZ)2^{-n}(n\in Z)的数累加得到的数,才能被精确表示。

显然0.1只能被近似表示,0.2只要将0.1乘2就行了,也就是说0.1和0.2的区别只是0.2的exponent多了1而已,这意味着形如0.1、0.2、0.4、0.8……的一组小数,即fraction相同的一组小数,取其中的某几个相加减,如果结果也在这组小数中,那么用“===”比较就可以得到“true”。例如“0.2 + 0.2 + 0.4 === 0.8”为true,但0.2、0.4、0.8皆为非精确表示。

0.3的fraction与它们不同,这种fraction不同的小数计算就会导致误差产生,这才使得“===”得到false。

参考

github.com/bartaz/ieee…

en.wikipedia.org/wiki/Double…