除法结果不正确
我有一个时间计算器,多年来一直运行良好。然而,一直困扰我的一件事是,如果使用小数秒,结果将成为浮点“错误”的牺牲品。因此,我最近转而使用这个 BigDecimal 库。
现在,我遇到了数学错误。以下是我今天收到的错误报告中的一个简化测试用例: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()
方法没有产生我期望的结果。我需要做哪些不同的事情?
更新
这里有一些更多细节,以回应评论。
首先,一些人认为使用 BigDecimal 太过分了。可能是这样,但我认为在做出决定之前需要更多细节。这个应用程序是一个时间计算器,所以有一些事情促使我切换到 BigDecimal。首先,因为这是一个计算器,所以向用户显示正确的答案很重要。如果用户输入 0.1 s + 0.2 s
,他们期望的答案是 0.3 s
,而不是 Javascript 将显示的答案 (0.30000000000000004
)。
我真的不想将精度限制在 JS 中可以使用的范围之外,以便我可以使用整数,因为我不知道用户需要的最大精度。我认为大多数人从不使用小数秒,但从我收到的电子邮件来看,有些人会这样做。我目前在内部将所有时间存储为秒。
有人建议我将数字存储为精确分数。不幸的是,我不知道这意味着什么。也许是因为我对数学了解不多。我的知识还不足以建立我自己的数学库;这就是我使用 BigDecimal 的原因。它已经存在很长时间了,所以我很犹豫是否说我的问题是由于 BigDecimal 中的错误造成的。我怀疑这是我使用它的方式中的一个错误。
最后,我并不是特别热衷于 BigDecimal。我愿意接受其他建议,只要我可以用我缺乏的数学技能来使用它们。
I've got a time calculator that has worked reasonably well for a number of years. One thing that always bothered me, though, was that if one used fractional seconds, the results would fall victim to floating-point "errors". So, I recently switched to using this BigDecimal library.
Now, I'm getting math errors. Here's a simplified test case from a bug report I received today: 27436 / 30418
is returning 1
instead of the expected 0.9019659412190151
.
To illustrate my problem, here's a Javascript console session in Chrome:
> 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"
As you can see, the divide()
method isn't producing the result I expect. What do I need to do differently?
Update
Here are some more details, in response to the comments.
First, several people suggested that using BigDecimal was overkill. That might be, but I think that more details are necessary before making that decision. This app is a time calculator, so there are a couple of things that pushed me to switch to BigDecimal. First, because this is a calculator, it's important to show the user a correct answer. If the user enters 0.1 s + 0.2 s
, they expect the answer to be 0.3 s
, not the answer that Javascript will show them (0.30000000000000004
).
I don't really want to limit the precision beyond what I can use in JS so that I can use integers, because I don't know the maximum precision my users need. Most never use fractional seconds, I think, but judging from the email I've received, some do. I'm currently storing all times internally as seconds.
Someone suggested that I store the numbers as exact fractions. Unfortunately, I don't know what that means. Perhaps it's because I don't know too much about math. I don't know enough to roll my own math library; that's why I'm using BigDecimal. It's been around for a long time, so I'm hesitant to say that my problem is due to a bug in BigDecimal. I suspect rather that it's a bug in the way I'm using it.
Finally, I'm not wedded to BigDecimal specifically. I'm open to other suggestions, provided that I can use them with my deficient math skills.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我还没有在任何生产代码中使用 BigDecimal,但发现这个问题很有趣,所以我尝试一下。关于需要
MathContext
作为除法函数的参数,您是正确的。以下是我根据您的示例所做的操作:创建一个上下文,告诉 BigDecimal 在科学模式输出中使用 100 位数字:
还有控制不同输出模式
PLAIN
、SCIENTIFIC 和
ENGINEERING
+ 各种舍入模式。jsfiddle 上的完整示例
更新:
默认输出格式是
SCIENTIFIC
,而不是PLAIN
。示例此处更新2:
此处创建了一个小型性能测试,看起来 BigDecimal 比本机 JavaScript 除法慢大约 10000 倍。
I haven't used BigDecimal in any production code yet but found this question interesting so I though I give it a try. You are right about the need for a
MathContext
as parameter to the division function. Here is what I did, based on your example:Creating a context that tells the BigDecimal to use 100 digits in scientific mode outputs:
There's also options to control different output modes
PLAIN
,SCIENTIFIC
andENGINEERING
+ various rounding modes.Full example on jsfiddle
Update:
The default output format is
SCIENTIFIC
, notPLAIN
. Examples hereUpdate 2:
Created a tiny performance test here, looks like BigDecimal is about 10000 times slower than native javascript division.