Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript 大数运算精度问题 #78

Open
pfan123 opened this issue Dec 25, 2020 · 0 comments
Open

JavaScript 大数运算精度问题 #78

pfan123 opened this issue Dec 25, 2020 · 0 comments

Comments

@pfan123
Copy link
Owner

pfan123 commented Dec 25, 2020

先从一个例子讲起

var number1 = 10000000000000000000000000 + 11111111111111111111111111   //理论上number1的值应该是21111111111111111111111111(javascript中会表示为科学计数法:2.111111111111111e+25)
var number2 = 21111111111111111111111000
console.log(number1 === number2)  //true

这个不用算简单看一下都知道计算结果不对,最后几位肯定应该都是1的,可是为什么会得到一个不对的值呢?

JavaScript Number的精度丢失问题

因为JavaScriptNumber类型是遵循 IEEE 754 规范表示的,这就意味着JavaScript能精确表示的数字是有限的,JavaScript可以精确到个位的最大整数是9007199254740992,也就是2的53次方,超过这个范围就会精度丢失,造成JavaScript无法判断大小,从而会出现下面的现象:

Math.pow(2, 53);    // 9007199254740992
Math.pow(2, 53) === Math.pow(2, 53) + 1;    // true
9007199254740992 === 9007199254740992 + 1;    // true

可以从下面这张图上看到JavaScript Number能够精确表示的上下限:

image

解决方案

当有两个整数 a 和 b ,在通常情况下我们有“+”运算符对其进行相加运算:

let sum = a + b;

但是 JS 在存放整数的时候是有一个安全范围的,一旦数字超过这个范围便会损失精度。

我们不能拿精度损失的数字进行运行,因为运算结果一样是会损失精度的。

所以,我们要用字符串来表示数据!(不会丢失精度)

JS 中整数的最大安全范围可以查到是:9007199254740991
假如我们要进行 9007199254740991 + 1234567899999999999

我们要先准备两个字符串变量和一个方法:

const a = "9007199254740991"
const b = "1234567899999999999"

function add(a ,b){
   //...
}

然后将字符串长度对齐:

const a = "9007199254740991"
const b = "1234567899999999999"

function add(a, b){
   //取两个数字的最大长度
   let maxLength = Math.max(a.length, b.length)
   //用0去补齐长度
   a = a.padStart(maxLength , 0)    //"0009007199254740991"
   b = b.padStart(maxLength , 0)   //"1234567899999999999"
}

然后从个位开始相加:

const a = "9007199254740991";
const b = "1234567899999999999";

function add(a, b) {
    if (typeof a === 'number') a = String(a)
    if (typeof b === 'number') b = String(b)

    //取两个数字的最大长度
    const maxLength = Math.max(a.length, b.length)

    //用0去补齐长度
    a = a.padStart(maxLength, 0)
    b = b.padStart(maxLength, 0)

    //定义加法过程中需要用到的变量
    let t = 0
    let f = 0  // 进位
    let sum = ''

    for(let i = maxLength; i--;) {
        t = parseInt(a[i]) + parseInt(b[i]) + f
        f = Math.floor(t/10)
        sum = t % 10 + sum
    }

    if (f === 1) {
        sum = '1' + sum
    }
    return sum
}

运行:

add(a ,b).    // 结果为:1243575099254740990

add(1234567899999999999, '1234567899999999999')   // 结果为: "2469135799999999999"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant