正确四舍五入数字的替代 d3.format?

Alternative d3.format that correctly rounds numbers?

提问人:prototype 提问时间:11/7/2023 最后编辑:prototype 更新时间:11/9/2023 访问量:52

问:

有没有基于 Math.round 而不是 Number.toFixed 的 d3.format 的替代方案?

我喜欢 d3.format,但它并没有像人们通过科学或其他舍入约定所期望的那样舍入 0.05(见 https://github.com/d3/d3-format/issues/27)

目前,d3.format 使用而不是内部使用,这会导致意外的舍入 https://github.com/d3/d3-format/issues/89Number.toFixedMath.round

因此,有时 0.05 向下舍入 () ,有时向上舍入 (),但它不遵循标准的科学规则,例如“四舍五入到偶数”,而是基于二进制表示8.95=>8.92.95=>3.0

[0.95,1.95,2.95,3.95,4.95,5.95,6.95,7.95,8.95,9.95]
returns
 ['0.9', '1.9', '3.0', '4.0', '5.0', '6.0', '7.0', '8.0', '8.9', '9.9']

因为

[0.95,1.95,2.95,3.95,4.95,5.95,6.95,7.95,8.95,9.95].map(v=>v.toFixed(16))
returns
0: "0.9500000000000000"
1: "1.9500000000000000"
2: "2.9500000000000002"
3: "3.9500000000000002"
4: "4.9500000000000002"
5: "5.9500000000000002"
6: "6.9500000000000002"
7: "7.9500000000000002"
8: "8.9499999999999993"
9: "9.9499999999999993"

我喜欢 d3.format 说明符语法令人难以置信的表达灵活性。我意识到有一个基于二进制表示的四舍五入的论点。但是很难说服商业用户,从技术上讲,这确实应该四舍五入,而 8.595 美元实际上是 8.5950000000000006,应该四舍五入。$8.995$8.9949999999999992$8.99

目标我的实际目标是在我们的应用程序中完全替换 d3.format,以便我们编写的代码依赖于 d3,以及我们导入的辅助库,例如 Plotly.js 和其他使用 d3 显示数字的四舍五入,因为我们都在小学学到,而不是我们中的一些人在研究生院学到。例如

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.js" </script>
<script src="https://some.other.size/libs/altd3.format.js"></script>
<script> d3.format = altd3.format </script>
D3.js 舍入

评论

2赞 BallpointBen 11/7/2023
d3 正确执行 0.05 舍入;问题在于,在(或任何东西)可以采取行动之前引入浮点错误。 确实小于 。v+0.95Math.round9.94999999999999939.95
0赞 prototype 11/7/2023
@BallpointBen。即使使用直接输入,也会发生这种情况,例如 返回,而我认为它应该四舍五入为 .例如,返回d3.format('0.1f')(8.95)"8.9""9.0"(Math.round(8.95*10)/10).toFixed(1)"9.0"
0赞 prototype 11/7/2023
我修改了示例以直接列出输入值。

答:

0赞 Michael Rovinsky 11/7/2023 #1

试试这个:

const round = (num, digits) => {
  const multiplier = Math.pow(10, digits);
  const value = Math.round(num * multiplier) / multiplier;
  return value.toFixed(digits);
}

console.log(round(3.14159265359, 3));
console.log(round(2.7182818284, 2));

更新

这是一个修改后的函数,将最后一位数字四舍五入为 或:05

const round = (num, digits) => {
  const multiplier = Math.pow(10, digits) * 2;
  const value = Math.round(num * multiplier) / multiplier;
  return value.toFixed(digits + 1);
}

// round(0.993212345, 2) = 0.995
// round(0.991234567, 2) = 0.990

评论

0赞 prototype 11/8/2023
正如我所期望的那样,这确实将 8.95 四舍五入到 9.0,相当于我期望的工作方式。但是,用户可能会指定大量可能的格式字符串,例如 (见 observablehq.com/@d3/d3-format?collection=@d3/d3-format),它们都会有奇怪的边缘情况,即以数字 5 结尾的分数不会按预期四舍五入。我的问题是,是否有 d3.format 的变体可以解析 d3 格式语法,但会像商业或科学消费者所期望的那样将 0.xx5 四舍五入?d3.format(".1f")["$.1f", "$.2f", ".2%", ".1f", ",.1f", ".1s", ".^20", ...]
0赞 Michael Rovinsky 11/8/2023
@prototype查看我更新的 Unser
0赞 prototype 11/9/2023
啊哈,我没有写用例。我的目标不仅仅是在我们控制代码的地方对数字进行四舍五入,而是让其他库(如 Plotly.js)使用 d3.format 将数字转换为显示字符串,也像最终用户所期望的那样四舍五入。
0赞 Michael Rovinsky 11/9/2023
@prototype我建议你发布一个关于第三方库问题的新问题,但我认为这根本不可能。