【问题标题】:How to not lose precision on division and multiplication?如何在除法和乘法上不丢失精度?
【发布时间】:2020-08-28 20:37:18
【问题描述】:

我有这样的操作:

const amount = 100;
const rate = 0.32123;

const a = amount * rate;
a // 32.123000000000005
const b = a / rate;
b // 100.00000000000001

转换后我需要数字相同。我怎样才能做到这一点?双向转换是必需的。我有很多货币。每种货币都有内部货币的兑换率(我们称之为 myCurr)。我无法立即将一种货币转换为另一种货币(内部货币除外),因为我仅对内部货币进行了转换评级。所以我需要首先将货币 A 转换为内部货币(A * rate1),然后我需要将 A 转换为目标货币,方法是将其除以 rate2。但这样做有时会导致精度下降。金额 * rate1 / rate2 有什么替代方案吗?

【问题讨论】:

  • Javascript 使用 IEEE-754 binary64。每次操作的误差最多为 2^53 的一部分。 amount * rate / rate 中的综合误差最多为 (1+2^−53)^2−1,略多于 2^52 的一部分。如果amount 是量化的——意味着它不能是任意值,但必须是某个最小货币数量的倍数(例如一美元或其他货币的千分之一),并且不能太大(因此它永远不会超过,比如说,2^50,大约一万亿,这些单位),那么可以对结果进行四舍五入以恢复原始值。
  • 请注意,如果 amount 允许使用分数,例如当货币为美元时,0.01 表示美分,那么它已经包含了一个舍入误差,应该在应用程序设计中加以考虑,因为 0.01 不能以 IEEE-754 binary64 表示。另请注意,上述注释适用于连续执行一次乘法和一次除法。如果还有其他操作,错误分析就更复杂了。

标签: javascript floating-point


【解决方案1】:

在将浮点数与任何算术运算及其逆运算一起使用后,您永远无法确保它保持不变。

原因:

浮点数涵盖了很大范围的数字 - 这就是为什么它们远不如整数精确

后果

  • 如果需要精度,请使用整数(四舍五入),如果不是,请使用浮点数。

  • 永远不要比较两个浮点数是否相等。如果比较它们,请检查它们是否接近。定义最大容差。如果数字的差异小于此容差,则认为数字相等。 (Math.abs(amount - b) <= tolerance)

【讨论】:

  • 这就是为什么在 java 中你总是使用equals() 方法而不是直接比较两个数字的原因之一
  • 关于“在将浮点数与任何算术运算及其反向运算一起使用后,您永远无法确保浮点数保持不变”:这太宽泛了。可以用浮点数进行精确的算术运算。这可能是一个小众用途,需要不经常使用的知识和技能,但这是可能的,并且 IEEE-754 旨在支持它(因此是“不精确”例外)。
  • 关于“它们远不如整数精确”:这不是一个准确的说法。例如,当 4 除以 3 时,整数运算将给出比浮点精度低许多数量级的结果。事实上,浮点数和整数算术的舍入问题是不同的,但它们都有问题,说一个比另一个准确或多或少是不正确的。
  • @EricPostpischil 不精确部分归咎于我尚未获得的知识以及我的语言技能,非常感谢你帮助我提高了两个^^
  • Re “如果你比较它们,检查它们是否接近”:This is not suitable advice for general use.
【解决方案2】:

您可以使用Math.floordouble tilde(~~)。这两种方法都能满足您的需求。

const amount = 100;
const rate = 0.32123;
const a = amount * rate;
const b = a / rate;
const t = ~~b;
const tt = Math.floor(b);

【讨论】:

  • Math.round() 更好。 Math.floor() 会将 12.9999999999 变成 12 而不是 13。
  • @Barmar 这取决于您的使用情况。 Math.round() 不应该被使用,如果 @Zendor18 与 0.1 或更少的差异很重要。然后他应该去获取差异并检查它是否小于特定的最大公差
  • @finnmglas 如果您正在比较值,那是真的。但这里的答案是为了生成结果,Math.floor()Math.ceil() 可以将一个微小的错误变成一个巨大的错误。
  • @Barmar 感谢您的精彩笔记,非常感谢。
猜你喜欢
  • 2012-05-13
  • 1970-01-01
  • 2021-09-27
  • 2011-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多