在Javascript中(在Chrome devtools控制台面板和Node.js v0.12.5中),我得到的这两个大数字的产品答案不正确:
输入:41962049 * 1827116622
输出:76669557221078480
在C ++和C#中,在将表达式转换为64位int时,我得到76669557221078478
的正确答案。
我假设这是一个整数溢出问题,但我当然可能是错的。
有没有办法在不使用像BigInteger这样的外部库的情况下在Javascript中为大数字获取准确的算术产品?这适用于不允许使用其他库的在线课程。
感谢您的帮助。
编辑:感谢解释这实际上不是整数溢出的解释,Patrick Roberts!很有用。
编辑2:jfriend00,我认为这个问题与你链接的问题不同,因为我试图弄清楚是否有办法解决JS的局限性而不依赖于外部库。您在评论中提供的答案有助于回答我的问题,谢谢!答案 0 :(得分:6)
这不是整数溢出,这是由于double precision floating point的限制。 JavaScript中最高的可准确表示的整数是2 ^ 53,因为2 ^ 52到2 ^ 53范围内的epsilon正好是1.在此之上,整数精度会中断,但乘法的结果不是由于整数溢出。
以下是维基百科关于IEEE 754标准的相关引用:
在2 52 = 4,503,599,627,370,496和2 53 = 9,007,199,254,740,992之间,可表示的数字正好是整数。对于下一个范围,从2 53 到2 54 ,一切都乘以2,因此可表示的数字是偶数,等等。相反,对于前一个范围, 2 51 至2 52 ,间距为0.5等。
作为2 n 至2 n + 1 范围内数字的一部分的间距是2 n-52 。将数字舍入到最接近的可表示的数字(机器epsilon)时的最大相对舍入误差因此为2 -53 。
要回答你的问题,这很可能。这是我刚刚在几个小时内编写的一个小型库,用于无符号整数加法和乘法,它能够显示基数为10的值:
class BigUint {
static get WORD() {
return 100000000000000;
}
static get HALF() {
return 10000000;
}
static map(word, index) {
if (index === 0) {
return word.toString();
}
return `000000${word}`.slice(-7);
}
static from(array) {
return Object.create(BigUint.prototype, {
words: {
configurable: true,
enumerable: true,
value: new Uint32Array(array),
writable: true
}
});
}
constructor(number) {
if (/\D/.test(`${number}`)) {
throw new TypeError('seed must be non-negative integer as number or string');
}
this.words = new Uint32Array(`${number}`.split(/(?=(?:.{7})+$)/).map(s => +s));
}
[Symbol.toPrimitive](hint) {
let string = this.toString();
switch (hint) {
case 'number':
return +string;
default:
return string;
}
}
get length() {
return this.words.length;
}
toString() {
return Array.from(this.words).map(BigUint.map).join('');
}
add(that) {
const thisLength = this.length;
const thatLength = that.length;
const maxLength = Math.max(thisLength, thatLength);
const minLength = Math.min(thisLength, thatLength);
const max = maxLength === thisLength ? this : that;
const words = [];
let augend, addend, sum, keep, carry = 0, index;
for (index = 1; index <= minLength; index++) {
augend = this.words[thisLength - index];
addend = that.words[thatLength - index];
sum = augend + addend + carry;
keep = sum % BigUint.HALF;
carry = (sum - keep) / BigUint.HALF;
words.unshift(keep);
}
for (; index <= maxLength; index++) {
sum = max.words[maxLength - index] + carry;
keep = sum % BigUint.HALF;
carry = (sum - keep) / BigUint.HALF;
words.unshift(keep);
}
if (carry > 0) {
words.unshift(carry);
}
return BigUint.from(words);
}
multiply(that) {
const thisLength = this.length;
const thatLength = that.length;
const minLength = Math.min(thisLength, thatLength);
const maxLength = Math.max(thisLength, thatLength);
const min = minLength === thisLength ? this.words : that.words;
const max = maxLength === thatLength ? that.words : this.words;
const partials = [];
let product, words, keep, carry = 0, sum, addend;
for (let outerIndex = minLength - 1; outerIndex >= 0; outerIndex--) {
words = [];
partials.push(words);
for (let j = minLength - 1; j > outerIndex; j--) {
words.unshift(0);
}
for (let innerIndex = maxLength - 1; innerIndex >= 0; innerIndex--) {
product = min[outerIndex] * max[innerIndex] + carry;
keep = product % BigUint.HALF;
carry = (product - keep) / BigUint.HALF;
words.unshift(keep);
}
if (carry > 0) {
words.unshift(carry);
carry = 0;
}
}
sum = BigUint.from(partials.pop());
while (partials.length > 0) {
sum = sum.add(BigUint.from(partials.pop()));
}
return sum;
}
}
const a = document.getElementById('a');
const b = document.getElementById('b');
const c = document.getElementById('c');
const op = document.querySelector('select');
function calculate() {
c.textContent = new BigUint(a.value)[op.value](new BigUint(b.value));
}
document.addEventListener('input', calculate);
calculate();
&#13;
<input id="a" type="number" min="0" step="1" value="41962049">
<select>
<option value="add">+</option>
<option value="multiply" selected>×</option>
</select>
<input id="b" type="number" min="0" step="1" value="1827116622">
=
<span id="c"></span>
&#13;