简体   繁体   中英

Incorrect division results

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM