JavaScript 所有数字都保存成 64 位浮点数,这给数值的表示带来了两大限制。
一是数值的精度只能到 53 个二进制位(相当于 16 个十进制位),大于这个范围的整数,JavaScript 是无法精确表示,这使得 JavaScript 不适合进行科学和金融方面的精确计算。
二是大于或等于2的1024次方的数值,JavaScript 无法表示,会返回Infinity。
// 超过 53 个二进制位的数值,无法保持精度
Math.pow(2, 53) === Math.pow(2, 53) + 1 // true
// 超过 2 的 1024 次方的数值,无法表示
Math.pow(2, 1024) // Infinity
问题场景:JSON.parse中遇到的BIGINT
比较常见的场景为后台返回了一串JSON但是数字为BIGINT导致解析错误,一般为16位
var c='{"num": 90071992547409999}'
JSON.parse(c) // {num: 90071992547410000}
解决办法:
JavaScript 中的 Number 是双精度浮点型,这意味着精度有限。
Number.MAX_SAFE_INTEGER 就是安全范围内的最大值,为 2**53-1。
最小安全值为 Number.MIN_SAFE_INTEGER 值为 -((2**53)-1)。
超出安全值的计算都会丧失精度。
如下,可以看到 max + 1 与 max + 2 的值相同,这显然是不对的。
const max = Number.MAX_SAFE_INTEGER; // 9007199254740991
max + 1 // 9007199254740992
max + 2 // 9007199254740992
ECMAScript 中的提案 BigInt 就是一个可以表示任意精度的新的数字原始类型。
BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。
为了与 Number 类型区别,BigInt 类型的数据必须添加后缀n。
1234 // 普通整数
1234n // BigInt
// BigInt 的运算
1n + 2n // 3n
JavaScript 原生提供BigInt函数,可以用它生成 BigInt 类型的数值。
转换规则基本与Number()一致,将其他类型的值转为 BigInt。
BigInt(123) // 123n
BigInt('123') // 123n
BigInt(false) // 0n
BigInt(true) // 1n
const a = 2172141653n;
const b = 15346349309n;
// BigInt 可以保持精度
a * b // 33334444555566667777n
// 普通整数无法保持精度
Number(a) * Number(b) // 33334444555566670000
题目练习:
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
输入:digits = [1,2,3] 输出:[1,2,4] 解释:输入数组表示数字 123。
var plusOne = function(digits) {
// let number = 0
// for(let i = 0;i<=digits.length-1;i++){
// number += digits[i] * (10 ** (digits.length-i-1))
// }
// return [...(number+1).toString()]
// 存在精度问题,所以此方法行不通
// 输入:
// [6,1,4,5,3,9,0,1,9,5,1,8,6,7,0,5,5,4,3]
// 输出:
// [6,1,4,5,3,9,0,1,9,5,1,8,6,7,0,5,0,0,0]
// 预期结果:
// [6,1,4,5,3,9,0,1,9,5,1,8,6,7,0,5,5,4,4]
let num=BigInt(digits.join(''))+1n
return num.toString().split('')
};