4. 五种异常
无效运算 > 上溢 > 除以零 > 下溢 > 不精确,前三个一旦出现很少被忽略
IEEE 754 定义了五种基本的浮点异常,分别是:无效运算、除以零、上溢(指数太大)、下溢(指数太小)和不精确。前三个一旦出现很少被忽略,后两个通常可以安全地忽略(尽管不总是)。
异常的优先顺序是:无效运算 > 上溢 > 除以零 > 下溢 > 不精确。能够在单个运算中同时发生的标准异常只有不精确的上溢和不精确的下溢这两种组合。
1. 无效运算
示例:0/0, 0*∞, ∞/∞, 负数的平方根, NaN
参与的任何运算, 无效转换
0 / 0; // NaN
0 / -0; // NaN
-0 / 0; // NaN
-0 / -0; // NaN
0 * Infinity; // NaN
0 * -Infinity; // NaN
-0 * Infinity; // NaN
-0 * -Infinity; // NaN
Infinity / Infinity; // NaN
Infinity / -Infinity; // NaN
-Infinity / Infinity; // NaN
-Infinity / -Infinity; // NaN
Math.sqrt(-1); // NaN
NaN + 1; // NaN
parseInt("hello") + 1; //NaN
parseInt("hello"); // NaN
parseInt(NaN); // NaN
parseInt(Infinity); // NaN
2. 除以零
原因:针对有限操作数执行运算时,生成精确的无穷大结果
1 / 0; // Infinity
1 / -0; // -Infinity
-1 / 0; // -Infinity
-1 / -0; // Infinity
Math.log(0); // -Infinity
Math.log(-0); // -Infinity
3. 上溢(指数太大)
原因:正确舍入的结果的指数,超过了能表示的最大指数值,即指数 > emax。
双精度示例:21024,e709.8, MAX_VALUE*2
缺省结果:取决于舍入模式和中间结果的符号。四种舍入方向:
向最接近的可表示的值舍入:当有两个最接近的可表示的值时,首选“偶数”值
// 舍入模式是“最近”
Math.pow(2, 1024); // Infinity
Math.exp(709.8); // Infinity
Number.MAX_VALUE * 2; // Infinity
Number.MAX_VALUE * -2; // -Infinity
4. 下溢(指数太小)
原因:精确结果或正确舍入的结果的绝对值,比能表示的最小正“正规数”小,即 |结果| < 最小正“正规数”。
双精度示例:2−1074,2−1075
下溢,也称浮点下溢/算术下溢。在《1. 双精度浮点数》中我们有提到,IEEE 754 binary64(双精度浮点数)实际存储的有效数是经过规范化处理的。正规数,就是规范化处理后的有效位表示,其有效数的默认前导位是 1。
次正规数是介于最小正规数和零之间的数,它通过调整指数(将指数位全置为 0)来去除有效数的前导 1,以此来表示比最小的正规数更接近 0 的数字。这样就能填补浮点运算中 0 附近的下溢间隙了,进而避免“即使两个数值不相等,减法 a - b 会发生下溢进而产生 0”的情况(达到下溢时,会丢弃所有有效数字,然后就突然变 0 了)。所以,次正规数也被称为逐渐下溢,因为它允许(值非常小的)计算结果慢慢地失去精度。
4.1 最小正规数
最小的正“正规数”应该是:正数即符号位为 0,11 位指数是“全 0+1”,52 位有效数是“全 0”。也就是说其 binary64 的内存存储为 0x0010000000000000
,如下:
真实值就是:(−1)0∗1.00...00(2)∗21−1023=1∗2−1022≈2.2250738585072014e−308
2**-1022; // 2.2250738585072014e-308
4.2 最大次正规数
最大“次正规数”应该是:正数即符号位为 0,11 位指数是“全 0”,52 位有效数是“全 1”。也就是说其 binary64 的内存存储为 0x000FFFFFFFFFFFFF
,如下:
真实值就是:(−1)0∗0.11...11(2)∗2−1022
= 11...111(2)∗2−1022−52
= (252−1)∗2−1022−52
= 2−1022−2−1074
Number(2**-1022 - 2**-1074); // 2.225073858507201e-308
Number(2**-1022 - 2**-1074).toPrecision(55); // '2.225073858507200889024586876085859887650423112240959465e-308'
4.3 最小次正规数
最小“次正规数”应该是:正数即符号位为 0,11 位指数是“全 0”,52 位有效数是“全 0+1”。也就是说其 binary64 的存储存储为 0x0000000000000001
,如下:
真实值就是:(−1)0∗0.00...01(2)∗2−1022
= 1∗1(2)∗2−1022−52
2**-1074; // 5e-324
Number(2**-1074).toPrecision(70); // '4.940656458412465441765687928682213723650598026143247644255856825006755e-324'
4.4 缺省结果
下溢的缺省结果:返回次正规数或 0。示例如下:
// 用最小正规数除以2,可以构造出次正规数
2**-1022 / 2; // 1.1125369292536007e-308 < 最大次正规数 2.225073858507201e-308
// 用最小次正规数除以2,可以构造出超出浮点数能表示范围的数,此时会返回 0
2**-1074 / 2; // 0
5. 不精确
原因:有效运算的舍入结果和无限精确结果不相等,即精度被裁剪了。实际上,大多数浮点运算都会导致“不精确”异常,详见《2. 精度损失》。
/// 舍入
1/3; // 0.3333333333333333, 实际值是无限循环小数(有理数)
Math.log(1.1); // 0.09531017980432493
/// 上溢
2**1024; // Infinity,指数超出最大值了 [-1022, +1023]
/// 下溢
2**-1022 / 2; // 用最小正规数除以2,可以构造出次正规数
2**-1074 / 2; // 用最小次正规数除以2,可以构造出超出浮点数能表示范围的数,此时会返回 0
6. 主要参考