学习链接
数据类型-Number
函数 toFixed(n)
将数字舍入到小数点后 n
位,并以字符串形式返回结果。
浮点数计算
在内部,数字全是以 64 位格式浮点数形式存储的,需要整数运算时,转为 32 位整数再计算。
1 位用于符号位,11 位用于存储指数部分,52 位用于存储小数部分(即有效数字)。
15 位的十进制数都可精确处理,超过 时不保证精度。
浮点数的精度损失
使用二进制数字系统无法 精确 存储 0.1 或 0.2,就像没有办法将三分之一存储为十进制小数一样。
因为 1/3
无法用 10
的整数次幂来进行精确表示,0.1
和 0.2
也无法用 2
的整数次幂来进行精确表示。
IEEE-754 数字格式通过将数字舍入到最接近的可能数字来解决此问题。
这些舍入规则容易让人们忽略掉“极小的精度损失”,但是它确实存在。
0.1 + 0.2 !== 0.3 // true
0.1 + 0.2 // 0.30000000000000004
0.1.toFixed(20) // '0.10000000000000000555'
0.2.toFixed(20) // '0.20000000000000001110'
(0.1 + 0.2).toFixed(20) // '0.30000000000000004441'
0.3.toFixed(20) // '0.29999999999999998890'
当我们对两个数字进行求和时,它们的“精度损失”会叠加起来。
这就是为什么 0.1 + 0.2
不等于 0.3
。
解决方案
最可靠的方法是借助方法 toFixed(n)
对结果进行舍入
let sum = 0.1 + 0.2;
sum.toFixed(2) // '0.30'
请注意,toFixed
总是返回一个字符串。
我们可以将数字临时乘以 100(或更大的数字),将其转换为整数,进行数学运算,然后再除回。当我们使用整数进行数学运算时,误差会有所减少,但仍然可以在除法中得到:
(0.1 * 10 + 0.2 * 10) / 10 // 0.3
(0.28 * 100 + 0.14 * 100) / 100 // 0.4200000000000001
因此,乘/除法可以减少误差,但不能完全消除误差。
补充
9999999999999999 // 显示 10000000000000000
出现了同样的问题:精度损失。有 64 位来表示该数字,其中 52 位可用于表示有效数字,但这还不够。所以最不重要的数字就消失了。
JavaScript 不会在此类事件中触发 error。它会尽最大努力使数字符合所需的格式,但不幸的是,这种格式不够大到满足需求。
常规数字检测
isNaN(value)
先将其参数转换为数字(调用Number()
),然后检测它是否为NaN
isFinite(value)
先将其参数转换为数字(调用Number()
),如果它是常规数字,而不是NaN/Infinity/-Infinity
,则返回true
Number.isNaN(value)
检查一个值是否为NaN
,只对数值有效,不进行转换Number.isFinite(value)
检查一个值是否为常规数字,只对数值有效,不进行转换
isNaN(NaN) // true
isNaN("NaN") // true
Number.isNaN(NaN) // true
Number.isNaN("NaN") // false
Number.isNaN(1) // false
isFinite(25) // true
isFinite("25") // true
Number.isFinite(25) // true
Number.isFinite("25") // false
字符串转数字
“硬” 转换
使用加号 +
或 Number()
的数字转换是严格的。如果一个值不完全是一个数字,就会失败
+"100px" // NaN
唯一的例外是字符串开头或结尾的空格,因为它们会被忽略。
“软” 转换
使用 parseInt/parseFloat
进行“软”转换,它从字符串中读取数字,直到无法读取为止,然后返回在发生 error 前可以读取到的值。
(与 Number.parseInt/Number.parseFloat
完全一致)
函数 parseInt
返回一个整数,而 parseFloat
返回一个浮点数
parseInt(' 100px ')// 100
parseFloat(' 12.5em') // 12.5
parseInt(' 12.3 ') // 12,只有整数部分被返回了
parseFloat(' 12.3.4 ') // 12.3,在第二个点出停止了读取
parseInt('a123') // NaN,第一个符号停止了读取
parseInt(str, radix)
parseInt()
函数具有可选的第二个参数。它指定了数字系统的基数,因此 parseInt
还可以解析十六进制数字、二进制数字等的字符串:
alert( parseInt('0xff', 16) ); // 255
alert( parseInt('ff', 16) ); // 255,没有 0x 仍然有效
alert( parseInt('2n9c', 36) ); // 123456
特殊数值
+0
和 -0
-0 === +0 // true
0 === -0 // true
0 === +0 // true
+0 // 0
-0 // 0
(-0).toString() // '0'
(+0).toString() // '0'
(1 / +0) === (1 / -0) // false
(1 / +0) // Infinity
(1 / -0) // -Infinity
NaN
Not a Number
typeof NaN // 'number'
NaN === NaN // false
5 - 'x' // NaN
Math.sqrt(-1) // NaN
0 / 0 // NaN
Infinity
和 -Infinity
1/0 // Infinity
-1/0 // -Infinity
// 数学计算中当作无穷
0 * Infinity // NaN
0 / Infinity // 0
Infinity / 0 // Infinity
Infinity + Infinity // Infinity
Infinity * Infinity // Infinity
Infinity - Infinity // NaN
Infinity / Infinity // NaN
// 与 undefined 计算都是 NaN
Object.is()
有一个特殊的内建方法 Object.is
,它类似于 ===
一样对值进行比较,但它对于两种边缘情况更可靠:
NaN
:Object.is(NaN, NaN) === true
0
和-0
:Object.is(0, -0) === false
在所有其他情况下,Object.is(a, b)
与 a === b
相同。
这种比较方式经常被用在 JavaScript 规范中。当内部算法需要比较两个值是否完全相同时,它使用 Object.is
(内部称为 SameValue)。