【问题标题】:Incorrect division results不正确的除法结果
【发布时间】:2012-02-02 23:33:22
【问题描述】:

我有一个多年来运行良好的时间计算器。但是,一直困扰我的一件事是,如果使用小数秒,结果将成为浮点“错误”的牺牲品。所以,我最近改用this BigDecimal library

现在,我遇到了数学错误。这是我今天收到的错误报告中的简化测试用例:27436 / 30418 正在返回 1 而不是预期的 0.9019659412190151

为了说明我的问题,这是 Chrome 中的 Javascript 控制台会话:

> first = 27436
27436
> second = 30418
30418
> first / second
0.9019659412190151   // expected result, but using JS numbers, not BigDecimals
> firstB = new BigDecimal(first.toString())
o
> secondB = new BigDecimal(second.toString())
o
> firstB / secondB
0.9019659412190151 // this is a JS number, not a BigDecimal, so it's susceptible to the problems associated with floating-point.
> firstB.divide(secondB)
o  // BigDecimal object
> firstB.divide(secondB).toString()
"1"  // huh? Why 1?
> firstB.divideInteger(secondB).toString()
"0"

如您所见,divide() 方法没有产生我期望的结果。我需要做些什么不同的事情?

更新

这里有一些更多的细节,以回应 cmets。

首先,有几个人认为使用 BigDecimal 太过分了。可能是这样,但我认为在做出决定之前需要更多细节。这个应用程序是time calculator,所以有几件事促使我切换到 BigDecimal。首先,因为这是一个计算器,所以向用户显示正确答案很重要。如果用户输入 0.1 s + 0.2 s,他们希望答案是 0.3 s,而不是 Javascript 将显示的答案 (0.30000000000000004)。

我真的不想将精度限制在我可以在 JS 中使用的范围之外,以便我可以使用整数,因为我不知道我的用户需要的最大精度。我认为大多数人从不使用小数秒,但从我收到的电子邮件来看,有些人会这样做。我目前在内部将所有时间存储为秒。

有人建议我将数字存储为精确分数。不幸的是,我不知道那是什么意思。也许是因为我对数学不太了解。我对自己的数学库知之甚少;这就是我使用 BigDecimal 的原因。它已经存在了很长时间,所以我不敢说我​​的问题是由于 BigDecimal 中的一个错误。我怀疑这是我使用它的方式中的一个错误。

最后,我并没有特别喜欢 BigDecimal。我愿意接受其他建议,前提是我可以将它们用于我缺乏的数学技能。

【问题讨论】:

  • 可能与精度有关。就像,如果精度太小,它只会四舍五入到最接近的整数。此外,仅仅因为浮点错误而使用 BigDecimal 似乎有点矫枉过正。您也可以将数字存储为精确的分数。
  • 是的,使用 BigDecimal 对我来说也有点矫枉过正。尤其是如果您要对 time 等固定精度的东西进行计算,为什么不只使用整数(秒?毫秒?..?)然后进行所有适当的转换..?跨度>
  • 0.1 对于舍入误差来说是一个相当大的差异,问题仍然存在于萤火虫中。您可能想提交错误报告。
  • @j08691:您能否解释一下为什么发布该链接以及它对我的情况有何帮助?正如我在帖子中解释的那样,我已经知道浮点错误的原因。我正在为我的特定问题寻找解决方案,但我看不到您的链接如何解决我的问题。

标签: javascript math bigdecimal division


【解决方案1】:

我还没有在任何生产代码中使用 BigDecimal,但发现这个问题很有趣,所以我尝试了一下。你是对的,需要一个 MathContext 作为除法函数的参数。根据您的示例,这是我所做的:

console.log(firstB.divide(secondB, new MathContext(100)).toString());

创建一个上下文,告诉 BigDecimal 在科学模式输出中使用 100 位数字:

0.9019659412190150568742192123085015451377473864159379314879347754618975606548754027220724570977710566

还可以选择控制不同的输出模式PLAINSCIENTIFICENGINEERING + 各种舍入模式。

jsfiddle 上的完整示例

更新: 默认输出格式为SCIENTIFIC,而不是PLAIN。例子here

更新 2: 创建了一个微小的性能测试here,看起来 BigDecimal 比原生 javascript 除法慢了大约 10000 倍。

【讨论】:

  • 谢谢。将我的 BigDecimal 副本更新到最新版本后,一切都开始工作了。
  • 嗯,性能并不是太关键。我只做一次除法操作。总的来说,我的应用程序会根据用户输入进行大约五次计算,因此速度差异不会很明显。我们说的可能相差几毫秒。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-24
  • 1970-01-01
  • 2016-05-02
  • 1970-01-01
  • 2014-06-19
  • 2019-09-21
  • 2013-03-04
相关资源
最近更新 更多