# BigInt

JavaScript 所有数字都保存成 64 位浮点数,这给数值的表示带来了两大限制。一是数值的精度只能到 53 个二进制位(相当于 16 个十进制位),大于这个范围的整数,JavaScript 是无法精确表示,这使得 JavaScript 不适合进行科学和金融方面的精确计算。二是大于或等于 2 的 1024 次方的数值,JavaScript 无法表示,会返回 Infinity。

ES2020 引入了一种新的数据类型 BigInt(大整数),来解决这个问题,这是 ECMAScript 的第八种数据类型。BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。

# BigInt 类型

ES5: String, Number, Boolean, Null, Undefined
ES6 Added: Symbol, 6 types
ES10 added: BigInt, reaching 7 types

typeof 1n === 'bigint' // true
typeof BigInt('1') === 'bigint' // true
1
2

# BigInt & Number

过去,不支持大于 9007199254740992 的整数值。如果超过,该值将简单地锁定为 MAX_SAFE_INTEGER+1:

const limit = Number.MAX_SAFE_INTEGER
console.log(limit) // 9007199254740991
console.log(limit + 1) // 9007199254740992
console.log(limit + 2) // 9007199254740992
console.log(9007199254740993) // 9007199254740992
1
2
3
4
5

然后我们看看 BigInt,BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。

const larger = 9007199254740993n
console.log(larger) // 9007199254740993n
console.log(larger + 1n) // 9007199254740994n
console.log(larger * 1000n) // 9007199254740993000n
const largerNum = BigInt(9007199254740992)
const largerNum2 = BigInt(9007199254740993)
const largerStr = BigInt('9007199254740992')
const largerStr2 = BigInt('9007199254740993')
console.log(largerNum, largerNum2, largerStr, largerStr2) // 9007199254740992n 9007199254740992n 9007199254740992n 9007199254740993n
console.log(
  largerNum === largerStr,
  largerNum == largerStr,
  largerNum2 === largerStr
) // true true true
1
2
3
4
5
6
7
8
9
10
11
12
13
14

那么看看0n&0,1n&1对比如何

console.log(1n === 1, 1n == 1) // false true
console.log(0n === 0, 0n == 0) // false true
1
2

从上面式子不能看出 BigInt 值并不严格等于 Number 值。也就是不全等。

那两者的大小比较又会如何

1n < 2 // true
2n > 1 // true
2 > 2 // false
2n > 2 // false
2n >= 2 // true
1
2
3
4
5

# BigInt 计算

我们再来看看 MDN 的示例,发现 + * - % ** 这些运算符都支持

const previousMaxSafe = BigInt(Number.MAX_SAFE_INTEGER)
// 9007199254740991n

const maxPlusOne = previousMaxSafe + 1n
// 9007199254740992n

const theFuture = previousMaxSafe + 2n
// 9007199254740993n, this works now!

const multi = previousMaxSafe * 2n
// 18014398509481982n

const subtr = multi - 10n
// 18014398509481972n

const mod = multi % 10n
// 2n

const bigN = 2n ** 54n
// 18014398509481984n

bigN * -1n
// -18014398509481984n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

但是对于除法/和数字计算有些区别:

const expected = 4n / 2n
// 2n

const truncated = 5n / 2n
// 2n, not 2.5n
console.log(expected, truncated) // 2n 2n
1
2
3
4
5
6

# BigInt 正负号

BigInt 可以使用负号(-),但是不能使用正号(+),因为会与 asm.js 冲突。

;-42n + // 正确
  42n // 报错
1
2

# BigInt 函数

JavaScript 原生提供 BigInt 函数,可以用它生成 BigInt 类型的数值。转换规则基本与 Number()一致,将其他类型的值转为 BigInt。

BigInt(123) // 123n
BigInt('123') // 123n
BigInt(false) // 0n
BigInt(true) // 1n
1
2
3
4

BigInt()函数必须有参数,而且参数必须可以正常转为数值,下面的用法都会报错。

new BigInt() // TypeError
BigInt(undefined) //TypeError
BigInt(null) // TypeError
BigInt('123n') // SyntaxError
BigInt('abc') // SyntaxError
1
2
3
4
5

需要注意的是,BigInt 函数无法操作小数以及小数字符串

console.log(BigInt(123)) // 123n
BigInt(123.3) // RangeError: The number 123.3 cannot be converted to a BigInt because it is not an integer
BigInt(1.5) // RangeError
BigInt('1.5') // SyntaxError
1
2
3
4

BigInt 继承了 Object 对象的两个实例方法。

-- BigInt.prototype.toString()
-- BigInt.prototype.valueOf()

它还继承了 Number 对象的一个实例方法。

-- BigInt.prototype.toLocaleString()

console.log(typeof Object(1n)) // "object"
console.log(typeof Object(1n).valueOf()) // "bigint"

console.log(1024n.toString()) //"1024"
console.log(1024n.toString(2)) //"10000000000"
console.log(1024n.toString(16)) //"400"

const bigint = 123456789123456789n

// German uses period for thousands
console.log(bigint.toLocaleString('de-DE')) //"123.456.789.123.456.789"

// Request a currency format
console.log(
  bigint.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })
) //  "123.456.789.123.456.789,00 €"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

此外,还提供了三个静态方法。

-- BigInt.asUintN(width, BigInt): 给定的 BigInt 转为 0 到 2width - 1 之间对应的值。
-- BigInt.asIntN(width, BigInt):给定的 BigInt 转为 -2width - 1 到 2width - 1 - 1 之间对应的值。
-- BigInt.parseInt(string[, radix]):近似于 Number.parseInt(),将一个字符串转换成指定进制的 BigInt。

const max = 2n ** (64n - 1n) - 1n

BigInt.asIntN(64, max)
// 9223372036854775807n
BigInt.asIntN(64, max + 1n)
// -9223372036854775808n
BigInt.asUintN(64, max + 1n)
// 9223372036854775808n

const max2 = 2n ** (64n - 1n) - 1n

BigInt.asIntN(32, max2) // -1n
BigInt.asUintN(32, max2) // 4294967295n

// Number.parseInt() 与 BigInt.parseInt() 的对比
Number.parseInt('9007199254740993', 10)
// 9007199254740992
BigInt.parseInt('9007199254740993', 10)
// 9007199254740993n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# Math 对象扩展

ES6 在 Math 对象上新增了 17 个与数学相关的方法。所有这些方法都是静态方法,只能在 Math 对象上调用。

# Math.trunc()

Math.trunc()静态方法通过删除任何小数返回数字的整数部分。

console.log(Math.trunc(13.37)) //13

console.log(Math.trunc(42.84)) //42

console.log(Math.trunc(0.123)) //0

console.log(Math.trunc(-0.123)) // -0

console.log(Math.trunc('-1.123')) // -1
1
2
3
4
5
6
7
8
9

一些特殊的,但是能转成数值的处理

Math.trunc(true) //1
Math.trunc(false) // 0
Math.trunc(null) // 0
1
2
3

对于空值和无法截取整数的值,返回 NaN。

Math.trunc(NaN) // NaN
Math.trunc('foo') // NaN
Math.trunc() // NaN
Math.trunc(undefined) // NaN
1
2
3
4

处理Infinity/-Infinity,返回原值。

Math.trunc(-Infinity) // -Infinity
Math.trunc(Infinity) // Infinity
1
2

# Math.sign()

Math.sign 方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。

它会返回五种值。

  • 参数为正数,返回+1;
  • 参数为负数,返回-1;
  • 参数为 0,返回 0;
  • 参数为-0,返回-0;
  • 其他值,返回 NaN。
Math.sign(3) // 1
Math.sign(-3) // -1
Math.sign('-3') // -1
Math.sign(0) // 0
Math.sign(-0) // -0
Math.sign(NaN) // NaN
Math.sign('foo') // NaN
Math.sign() // NaN
1
2
3
4
5
6
7
8

如果参数是非数值,会自动转为数值。对于那些无法转为数值的值,会返回 NaN。

Math.sign('') // 0
Math.sign(true) // +1
Math.sign(false) // 0
Math.sign(null) // 0
Math.sign('9') // +1
Math.sign('foo') // NaN
Math.sign() // NaN
Math.sign(undefined) // NaN
1
2
3
4
5
6
7
8

# Math.hypot()

Math.hypot 方法返回所有参数的平方和的平方根。 如果参数不是数值,Math.hypot 方法会将其转为数值。只要有一个参数无法转为数值,就会返回 NaN。

Math.hypot(3, 4) // 5
Math.hypot(3, 4, 5) // 7.0710678118654755
Math.hypot() // 0
Math.hypot(NaN) // NaN
Math.hypot(3, 4, 'foo') // NaN
Math.hypot(3, 4, '5') // 7.0710678118654755
Math.hypot(-3) // 3
1
2
3
4
5
6
7

# Math.cbrt()

Math.cbrt()方法用于计算一个数的立方根。

Math.cbrt(-1) // -1
Math.cbrt(0) // 0
Math.cbrt(1) // 1
Math.cbrt(2) // 1.2599210498948732
1
2
3
4

对于非数值,Math.cbrt()方法内部也是先使用 Number()方法将其转为数值。

Math.cbrt('8') // 2
Math.cbrt('hello') // NaN
1
2

# 32 位转换方法

  • Math.clz32()方法将参数转为 32 位无符号整数的形式,然后返回这个 32 位值里面有多少个前导 0。
  • Math.imul方法返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。
  • Math.fround方法返回一个数的 32 位单精度浮点数形式。

# 对数方法

  • Math.expm1(x)返回 ex - 1,即 Math.exp(x) - 1。
  • Math.log1p(x)方法返回 1 + x 的自然对数,即 Math.log(1 + x)。如果 x 小于-1,返回 NaN。
  • Math.log10(x)返回以 10 为底的 x 的对数。如果 x 小于 0,则返回 NaN。
  • Math.log2(x)返回以 2 为底的 x 的对数。如果 x 小于 0,则返回 NaN。

# 双曲函数方法

  • Math.sinh(x) 返回 x 的双曲正弦(hyperbolic sine)
  • Math.cosh(x) 返回 x 的双曲余弦(hyperbolic cosine)
  • Math.tanh(x) 返回 x 的双曲正切(hyperbolic tangent)
  • Math.asinh(x) 返回 x 的反双曲正弦(inverse hyperbolic sine)
  • Math.acosh(x) 返回 x 的反双曲余弦(inverse hyperbolic cosine)
  • Math.atanh(x) 返回 x 的反双曲正切(inverse hyperbolic tangent)

# 二进制和八进制表示法

在 ES6 之前,对于整数的二进制表示,您最好的选择是将它们传递给基数为 2 的 parseInt。

parseInt('101', 2) //5
1

ES6 提供了二进制和八进制数值的新的写法,分别用前缀 0b(或 0B)和 0o(或 0O)表示。

0b111110111 === 503 // true
0o767 === 503 // true
0b111110111 === 503 // true
0o767 === 503 // true
1
2
3
4

ES6 延续了对以 10 为底的数字之外的数字文字表示方式的更改/变化。现在有了官方的八进制形式、修正的十六进制形式和全新的二进制形式。

let dec = 42,
  oct = 0o52, // or `0O52`
  hex = 0x2a, // or `0X2a`
  bin = 0b101010 // or `0B101010`
1
2
3
4

用 Number 处理这些字符串类型会被转化成字符串形式,

Number('42') // 42
Number('0o52') // 42
Number('0x2a') // 42
Number('0b101010') // 42
1
2
3
4

那么反过来处理会如何显示:

const a = 42

a.toString() // "42" -- also `a.toString( 10 )`
a.toString(8) // "52"
a.toString(16) // "2a"
a.toString(2) // "101010"
1
2
3
4
5
6

# 数值分隔符

ES2021,允许 JavaScript 的数值使用下划线(_)作为分隔符。

let num = 12_345;
console.log(num) // 12345
num.toString() // '12345'
1
2
3

数值分隔符有几个使用注意点。

  • 不能放在数值的最前面(leading)或最后面(trailing)。
  • 不能两个或两个以上的分隔符连在一起。
  • 小数点的前后不能有分隔符。
  • 科学计数法里面,表示指数的 e 或 E 前后不能有分隔符。
// 全部报错
3_.141
3._141
1_e12
1e_12
123__456
_1464301
1464301_
1
2
3
4
5
6
7
8

除了十进制,其他进制的数值也可以使用分隔符。

console.log(0b10_0) // 4
console.log(0b10_0) // 4
console.log(0o10_0) // 64
console.log(0o10_0) // 64
console.log(0x10_0) // 256
1
2
3
4
5
Last Updated: 4/1/2024, 11:24:19 PM