在完全使用 javascript 的 Web 堆栈中,从十进制货币源转换时如何避免浮点错误?

In a fully javascript web stack, how can floating point errors be avoided when converting from a currency source in decimal?

提问人:Kzqai 提问时间:5/23/2019 最后编辑:Kzqai 更新时间:5/23/2019 访问量:126

问:

我最近不得不设计一个完全使用javascript,express.js,REST apis,完整节点/浏览器js堆栈的资金处理应用程序。不幸的是,该应用程序处理了各种自定义计算,例如平均结果。当然,在对结果求平均值时,浮点加法和除法会发挥作用。因此,我会得到大约 0.12(12 美分!

从理论上讲,我了解处理浮点数的推荐选项:

  • 以整数计算
  • 在其他位置使用十进制数据类型

整数换货币的方法很好,这是有道理的,但不幸的是,如果请求以十进制形式传入,那么最初将浮点数转换为整数的某种计算仍然必须发生,并且仍然保留了出错的可能性。

因此,假设有一个请求被添加到美元价值列表中。 以干净的整数单位使用它会很好,但要到达那里需要先乘以 100,我怀疑这会导致整套错误随之而来。44.67

那么,在 javascript 中,处理十进制或美元请求之间的平滑转换以及避免浮点错误的计算的过程是什么?

在这种情况下,是否需要数据库,并且只需将值作为字符串传递到数据库中?或者有没有办法在 javascript 中将“无损”转换为整数?还是在处理金钱并要求准确性时有其他技术?

JavaScript 货币 浮动精度

评论

2赞 Bergi 5/23/2019
当然,您必须在乘以 100 后四舍五入为实际整数。这不会引入任何错误。
1赞 Jonas Wilms 5/23/2019
...换句话说:对于 1.23,将始终为 123Math.round(value * 100)
1赞 BenMorel 5/23/2019
问得好。如果您真的想避免浮点计算(这是正确的),并且假设输入字符串始终有 2 个十进制数字,则可以从输入中删除 并使用 转换为整数。IEEE 754(64 位)适用于最大 2^53 的整数(如果我没记错的话),因此您可以从那里以次要单位(美分)处理所有计算。只要你不分裂,你就被覆盖了。.parseInt()
1赞 Eric Postpischil 5/23/2019
@Bergi:“没有引入错误”不是一个真实的说法。如果输入为“1000000000000001”,则解析输入的结果应产生1000000000000001,因为它可以精确表示,但将其乘以 100 将产生100000000000000096,四舍五入无法修复它。当然,OP不太可能使用一千万亿和一美元,但如果他们正在使用津巴布韦元,他们可能会这样做。
1赞 Eric Postpischil 5/23/2019
@Propagating:如果初始输入最多为 14 位十进制数字(包括小数点后两位),则乘以 100 并四舍五入到最接近的整数始终正好是初始输入的 100 倍。(实际上,我认为 15 位数字也是如此,但希望在断言之前起草一个证明。根据我上面的评论,OP 可能会遇到这个限制,但这不太可能。

答:

1赞 Eric Postpischil 5/23/2019 #1

问题中描述的问题中的潜在错误来源包括:

  • 输入值非常大,无法乘以 100 并四舍五入到最接近的整数而不会出错。(这需要大约 1013 或更大的输入值,因此除非数据涉及数万亿美元,否则不太可能。
  • 执行的计算可能会导致值过大,从而导致出现舍入错误。
  • 输入中提到的“自定义计算”包括具有舍入误差的内容,即使值是小整数。例如,提到了“平均”,并且 1 美分、1 美分和 2 美分的平均值不能在没有舍入误差的情况下以二进制浮点计算,因为 4/3 是不可表示的。此外,“自定义计算”可能包括对数、概率分布的评估、积分以及初等算术以外的其他数学。
  • 代码中可能存在错误。

那么在 [JavaScript] 中,处理十进制或美元请求之间的平滑转换以及避免浮点错误的计算的过程是什么?

关于前者,如果 x 是以十进制输入字符表示的数字,小数点后最多两位数,并且不太大(如上所述),并且是将该输入正确转换为 JavaScript 的结果,那么结果正好是 100x。没有净舍入误差。任何计算错误的根源都在别处。xNumberMath.round(100*x)