JavaScript 中的 64 位浮点数学,为什么在这种情况下除法比乘法更好

64bit floating point math in JavaScript, why does division work better than multiplication in this situation

提问人:samanthaj 提问时间:7/6/2022 最后编辑:Chrissamanthaj 更新时间:7/7/2022 访问量:53

问:

这不是关于浮点如何工作的问题。也不是关于为什么 19.95 * 0.1 产生 19.900000000004 的问题。我已经明白浮点数学不是一个完美的演示。问题在于向专家询问他们的直觉,为什么这种特殊情况会起作用。

最近,我想将数字四舍五入到任意数量,以便进行步进滑块。我有这个代码

 steppedV = Math.round(v / step) * step

我发现它不是很好,因为浮点数学问题。例如,返回v = 19.94step = 0.119.900000000000002

我以为我有点不走运,但后来只是为了好玩,我尝试了这个

 steppedV = Math.round(v / step) / (1 / step)

令人惊讶的是,对于我一直在使用的测试用例,它产生了更好的结果。对于与上面相同的输入,我回来了19.9

对于所有未四舍五入到我的测试步骤值的情况,它现在都正常工作。真正了解浮点数学如何工作的人可以解释为什么会产生更准确的结果而没有吗?作为一个了解浮点数学的人,这个结果直观吗?我只是运气好,还是作为专家,您知道要对(1 /步)进行除法以获得更好的结果吗?/ (1 / step)* step

function test(start, end, inc, step) {
  const addRow = makeTable(step, 'div', 'mult');
  for (let v = start; v <= end; v += inc) {
    const steppedVMult = Math.round(v / step) * step;
    const steppedVDiv = Math.round(v / step) / (1 / step);
    addRow(v, steppedVDiv, steppedVMult);
  }
}

test(0, 2, 0.03, 0.1);
test(0, 0.2, 0.003, 0.01);
test(0, 0.02, 0.0003, 0.001);
test(0, 0.002, 0.00003, 0.0001);

function c(tag, children = [], text = '') {
  const e = document.createElement(tag);
  e.textContent = text;
  children.forEach(c => e.appendChild(c));
  return e;
}
  
function makeTable(...args) {
  const makeRow = arr => c('tr', arr.map(v => c('td', [], v)));

  const tbody = c('tbody');
  const table = c('table', [
    c('thead', [makeRow(args)]),
    tbody,
  ]);
  document.body.appendChild(table);
  return function(...args) {
    tbody.appendChild(makeRow(args));
  };
}
body { font-family: monospace; }
table { border-collapse: collapse; margin-bottom: 10px }
thead { font-weight: bold; }
td { border: 1px solid #888; padding: 3px; }

JavaScript 浮点 精度

评论

0赞 Pointy 7/6/2022
19.900000000000002 非常接近 19.9
0赞 samanthaj 7/6/2022
这不是重点
0赞 Pointy 7/6/2022
二进制浮点值必须转换为十进制表示法才能显示。该过程决定要显示多少个小数位。确实,十进制浮点和二进制浮点是不兼容的,因此经常会出现明显的不一致。
0赞 Pointy 7/6/2022
此外,在其他情况下,您的解决方法可能会产生自己的意外结果。
1赞 slebetman 7/6/2022
0.1无法用浮点数正确表示。它的确切值是 。当您在某个时候乘以这个数字时,尾随值 (..5551115...等)会参与进来。但是,无法放入 64 位浮点数的确切值,因此结果不是由于舍入,而是由于尾部小数无法放入 64 位浮点格式。0.10000000000000000555111512312578270211815834045410156251/0.100000000000000005551115123125782702118158340454101562510.00000000000000000000000000000000000000000000000010

答: 暂无答案