重新学习 Number

334 阅读4分钟

js 中,Number 是一种 定义为 64位双精度浮点型(double-precision 64-bit floating point format) (IEEE 754) 的数字数据类型。在其他编程语言中,有不同的数字类型存在,比如:整型(Integers),单精度浮点型(Floats),双精度浮点型(Doubles),大数(Bignums),不过新的 js 也要有 BigInt 类型数字了。

提到 Number ,有两个概念:全局对象 Number ,和构造函数 Number ,全局对象 Number 由构造函数 Number() 创建。

本文主要涉及全局对象 Number 的几个静态属性。

MAX_SAFE_INTEGER 以及 MIN_SAFE_INTEGER

有一种诡异的接口数据错误,是后端发现自己没有错,但和前端看到的数据就是不一样。这时候往往需要考虑下是不是其中 Number 类型的数据超出范围了。

那么 js 中,整数类型的范围是什么呢?

-(2^53 - 1) 至 2^53 - 1 ,包含两个端点。

可以通过 Number 的静态属性 Number.MAX_SAFE_INTEGERNumber.MIN_SAFE_INTEGER来查看:

Number.MAX_SAFE_INTEGER // > 9007199254740991
9007199254740991 + 1 // > 9007199254740992
9007199254740992 + 1 // >9007199254740992

Number.MIN_SAFE_INTEGER // > -9007199254740991
-9007199254740991 - 1 // > -9007199254740992
-9007199254740992 - 1 // > -9007199254740992

9007199254740992 === 9007199254740993 // > true
9007199254740991 === 9007199254740992 // > false

-9007199254740992 === -9007199254740993 // > true
-9007199254740991 === -9007199254740992 // > false

可以看到,对于 -(2^53 - 1)2^53 - 1 范围外的数字,js 并不能做到“安全存储”,也就是无法准确分辨两个不相同的值。

json 解析接口返回的数据时,遇到 js 不能安全存储的值,强制转化为 Number 类型,字段的值就会被破坏。

对于可能超出整数类型范围的字段,一般的解决方案是改为 string 类型输出。

Number 提供了 isSafeInteger() 方法来判断被传递的值是否为安全整数。

Number.isSafeInteger(9007199254740992) // > false
Number.isSafeInteger(9007199254740991) // > true
Number.isSafeInteger(-9007199254740991) // > true
Number.isSafeInteger(-9007199254740992) // > false

不过新的 js 中定义了一种 BigInt 类型的数据,可以表示 大于 2^53 - 1 的数字:

BigInt(Number.MAX_SAFE_INTEGER + 2) + 1n // > 9007199254740993n

Number 类型的数字后面加上 n 可以定义一个 BigInt ,如上例;但 BigInt 和 Number 实例不能进行混合运算,因为 BigInt 变量转换成 Number 变量时可能会丢失精度:

BigInt(Number.MAX_SAFE_INTEGER + 2) + 1
// > VM261:1 Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
    at <anonymous>:1:37

MAX_VALUE 以及 MIN_VALUE

需要注意的是,Number.MIN_VALUENumber.MAX_VALUE 并不是 js 能表达的数值的范围,而是正数的范围。

Number.MAX_VALUE 表示 js 能表达的最大正值 ,超过这个数值即 Infinity

Number.MAX_VALUE  // > 1.7976931348623157e+308
1.7976931348623157e+309 // > Infinity

Number.MIN_VALUE 表示 js 能表达的最小正数,即最接近 0 但不会变成 0 的正数。

Number.MIN_VALUE  // > 5e-324

对于负数范围,可以如下表示:

-Number.MAX_VALUE  // > -1.7976931348623157e+308 最小负数
-Number.MIN_VALUE  // > -5e-324 最大负数

小于 -Number.MAX_VALUE 的值即为 -Infinity

-1.7976931348623157e+309
-Infinity

Infinity-Infinity 也可以用两个静态属性表示:

Number.NEGATIVE_INFINITY // > -Infinity
Number.POSITIVE_INFINITY  // > Infinity

NaN

Number.NaN 表示“非数字”,not a number ,编程中很少使用到 Number.NaN

全局对象中也有一个 NaN ,同时也有一个 isNaN() 函数。

同样,编程中也很少用 NaN ,毕竟即使 NaN 也是不同于 NaN 自己的,所有的自比较中,也只有 NaN 和自己不等(所以可以通过这个差异来实现 isNaN 函数:function isNaN(v) {return v !== v}):

NaN === NaN // > false
Number.NaN === NaN // false

我们通常用 isNaN() 或者 Number.isNaN() 来判断一个值是不是 Number 类型:

isNaN(NaN)  // > true
isNaN(Number.NaN) // > true
Number.isNaN(NaN) // > true
Number.isNaN(Number.NaN) // > true

那么,isNaN()Number.isNaN() 有区别吗?有,Number.isNaN() 仅当值为 NaN 时才返回 true ,而 isNaN() ,在值为 NaN 或者将值强制转换为数字后能得到 NaN 时,都是 true

isNaN('I am a string')  // > true
Number.isNaN('I am a string') // > false

EPSILON

Number.EPSILON 属性表示 1 与 Number 可表示的“大于 1 的最小浮点数”之间的差值。

这个静态属性有什么应用场景呢?首先来看一个很典型的例子:

0.1 + 0.2 // > 0.30000000000000004
0.1 + 0.2 === 0.3 // > false

假设 x = 0.1, y = 0.2, z = 0.3 ,我们怎么判断 x + y 是否等于 z

function isEqualFloat (a, b) {
    return Math.abs(a - b) < Number.EPSILON
}
isEqualFloat(x + y, z) // > true

是不是很有意思呢?