在JavaScript中,为什么这种行为具有大数字

时间:2018-03-27 12:14:42

标签: javascript

下面是三个函数,每个函数都做同样的事情,但有点不同。它们都将数组中的数字相乘并返回结果。但有一种奇怪的行为,有人可以解释一下有什么不同。

function _mreduce0(name) {
  return name.split('').reduce((acc, e) => {
    return acc * 31 * e.charCodeAt(0);
  }, 7);
}

function _mreduce1(name) {
  let nArr = name.split('');
  let acc = 7;
  for (let i = 0; i < nArr.length; i += 1) {
    acc *= 31 * nArr[i].charCodeAt(0);
  }
  return acc;
}

function _mreduce2(name) {
  let nArr = name.split('');
  let acc = 7;
  for (let i = 0; i < nArr.length; i += 1) {
    acc = acc * 31 * nArr[i].charCodeAt(0);
  }
  return acc;
}

let n = 'somename';

console.log(_mreduce0(n) === _mreduce1(n)); // true
console.log(_mreduce0(n) === _mreduce2(n)); // true


// It is good for small size but when the string is large there is difference.
n = 'e868831414410da6b0b416be7e80f5211765ad4d1aed295cd24fcc17e72c03fc';

console.log(_mreduce0(n) === _mreduce1(n)); // false
console.log(_mreduce0(n) === _mreduce2(n)); // true

3 个答案:

答案 0 :(得分:1)

Javascript只有一个类型 - 数字 - 而数字总是浮点类型。

它的意思是什么?它实际上可以跳过一些数字,或者没有精确的结果,计数太大,太低或十进制数。

什么是&#34;太大&#34;?有这个值:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER

如果你超过这个值(而你这样做),你就不能使用原生的javascript号码(你可以,但你可以得到不准确的结果)

答案 1 :(得分:1)

通常乘法是关联的,但在一种情况下,javascript引擎看起来像a = a*b; a = a*c;,而在另一种情况下它看起来tmp = (b*c); a = a*tmp;。在这两种情况下都会发生算术溢出,但是以不同的方式使它们的行为不同。

以下是显示相同效果的序列。差异发生,然后甚至自我解决,重新发生等等。

&#13;
&#13;
var x = 7;
var y = 7;

console.log(x === y);
for (var i = 1; i < 50; i++) {
  x *= 31 * 3;
  y = y * 31 * 3;
  console.log('i = ' + i + ' -> ' + (x === y ? "equal" : "diff: " + (x - y)));
}
&#13;
&#13;
&#13;

如果我们将其更改为使用y = y * 93,则永远不会有任何区别:

&#13;
&#13;
var x = 7;
var y = 7;

console.log(x === y);
for (var i = 1; i < 50; i++) {
  x *= 31 * 3;
  y = y * 93;
  console.log('i = ' + i + ' -> ' + (x === y ? "equal" : "diff: " + (x - y)));
}
&#13;
&#13;
&#13;

我的结论是y = y * 31 * 3从左到右计算右前端,每个*后可能出现算术错误效应。

y *= 31 * 3实际上也是这样做的,但右侧是31 * 3 === 93,然后乘以y

答案 2 :(得分:1)

比较12怎么样?这也是错误的,所以看起来02正在做类似的事情。 acc *=表达式似乎是罪魁祸首。

请参阅:Why are shortcuts like x += y considered good practice?

您在迭代8开始溢出,因为值不匹配。

7.43088268730786e+26 !== 7.430882687307858e+26

这两个数字之间的差异是137,438,953,472。

完整演示

var valTable, n = 'e868831414410da6b0b416be7e80f5211765ad4d1aed295cd24fcc17e72c03fc';

valTable = [];
console.log('Are they the same?:', _mreduce0(n) === _mreduce2(n)); // true
console.log(displayValues(valTable));

valTable = [];
console.log('Are they the same?:', _mreduce0(n) === _mreduce1(n)); // false
console.log(displayValues(valTable));

function _mreduce0(name) {
  return name.split('').reduce((acc, e, i) => {
    var val = acc * 31 * e.charCodeAt(0);
    addToTable(valTable, i, 0, val); // Control
    return val;
  }, 7);
}

function _mreduce1(name) {
  let nArr = name.split(''), acc = 7;
  for (let i = 0; i < nArr.length; i += 1) {
    acc *= 31 * nArr[i].charCodeAt(0);
    addToTable(valTable, i, 1, acc); // Loop 1
  }
  return acc;
}

function _mreduce2(name) {
  let nArr = name.split(''), acc = 7;
  for (let i = 0; i < nArr.length; i += 1) {
    acc = acc * 31 * nArr[i].charCodeAt(0);
    addToTable(valTable, i, 1, acc); // Loop 2
  }
  return acc;
}

function addToTable(table, row, col, val) {
  if (table.length < row + 1) table.push([val]); else table[row][col] = val;
}

function displayValues(table) {
  return table.map((v, i) => [i, v[0] === v[1] ? 'yes' : 'no', v[0], v[1]].join('\t')).join('\n');
}
.as-console-wrapper { top:0; max-height:100% !important;}