除法结果不正确 [英] Incorrect division results

查看:236
本文介绍了除法结果不正确的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个时间计算器,多年来运行良好.不过,令我困扰的一件事是,如果使用小数秒,结果将成为浮点错误"的牺牲品.因此,我最近改用了此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.我愿意接受其他建议,但前提是我可以将其与缺乏数学技能的人一起使用.

解决方案

我尚未在任何生产代码中使用BigDecimal,但发现此问题很有趣,因此尽管我尝试一下.您对MathContext作为除法函数的参数的需求是正确的.根据您的示例,这就是我所做的:

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

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

0.9019659412190150568742192123085015451377473864159379314879347754618975606548754027220724570977710566

还有一些选项可以控制不同的输出模式PLAINSCIENTIFICENGINEERING +各种取整模式.

jsfiddle

上的完整示例

更新: 默认输出格式为SCIENTIFIC,而不是PLAIN.示例此处

更新2:此处创建了一个微小的性能测试,看起来BigDecimal的速度比本地javascript划分慢了大约10000倍. /p>

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.

解决方案

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:

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

Creating a context that tells the BigDecimal to use 100 digits in scientific mode outputs:

0.9019659412190150568742192123085015451377473864159379314879347754618975606548754027220724570977710566

There's also options to control different output modes PLAIN, SCIENTIFIC and ENGINEERING + various rounding modes.

Full example on jsfiddle

Update: The default output format is SCIENTIFIC, not PLAIN. Examples here

Update 2: Created a tiny performance test here, looks like BigDecimal is about 10000 times slower than native javascript division.

这篇关于除法结果不正确的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆