Win a copy of Re-engineering Legacy Software this week in the Refactoring forum
or Docker in Action in the Cloud/Virtualization forum!
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic

Rounding Error in a calculateLitresPer100Km method - read on for more details

 
Vadim Konkov
Greenhorn
Posts: 8
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi, I'm just looking for some help on how to fix the rounding error in the following method:



In the JUnitTest scenario, kilometers is 123.4 and litres is 10.5 - JUnitTest expects 8.502024291497976E-4 but gets 8.502024291497975

All variables used in above code are doubles.

I am thinking there is some sort of easy solution to this that I'm overlooking. The rest of my code does not affect this method whatsoever.

Thanks for looking and any potential help.
 
Mike Simmons
Ranch Hand
Posts: 3028
10
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Have you looked at the JavaDoc API for the assertEquals() method you're using? (At least I assume you're using one, though it's not entirely clear what you're doing here.) Make sure you get the right types of the parameters - they're both doubles, right? The docs should give you a hint about what to do instead.


 
Vadim Konkov
Greenhorn
Posts: 8
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks for the reply but that won't work for me. Here is the whole code if it will help make the problem clearer. Also the JUnitTest cases would substitute a double value for the kilometers and litres, and than expects a certain answer for the litresPer100Km to test if it is coded properly.

 
Mike Simmons
Ranch Hand
Posts: 3028
10
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think we need to see what you're doing in the test code. I'm pretty sure that there is an assertEquals() method that will help you, if you look carefully.
 
Tom Johnson
Ranch Hand
Posts: 142
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Please please please change your code to use BigDecimal or you will be chasing errors like this for a long long time. See my post here for issues I was having.

If you change to use BigDecimal, you can specify both the scale (number of decimal places) of the operations and also the mode of rounding e.g, BigDecimal.ROUND_HALF_UP. This will cleanup these errors for you. Note, always use the BigDecimal constructor that takes a String, not a double as this one leads to imprecise values.
 
Mike Simmons
Ranch Hand
Posts: 3028
10
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I disagree. Tom, you were dealing with numbers representing money, which is something people expect to be reduced to a nice simple two digits after the decimal point. Ten dollars divided three ways gives each person $3.33, while the programmer figures out how to pocket the roundoff error and make millions. (C.F. Office Space and Superman III.) But look at the quantities he's dealing with in the code above. They're real numbers, meaning not integers. And not "decimal" values which are really integers divided by a power of ten. They've got lots of digits, and while it's usually unlikely that more than the first three or four are actually of interest to anyone, there's also no reason to throw away accuracy prematurely - e.g. by rounding 10/3 to 3.33, when 3.33333333333333333 is more accurate. In other words, these are the sorts of calculations where programmers should use double, or occasionally float - floating point primitives, in both cases.

Calculations with primitives offer several advantages in Java. For starters, they're easier to read, since you don't have to write things like x.add(y.multiply(z)) when you can write x + y * z. (And that was a minimal example.) They're also significantly faster and less memory-intensive, if those concerns are actually pertinent. (In this day and age, they usually aren't, unless you're doing a lot of math.) And they don't round off values needlessly just to provide a false sense of security for people who don't want to see a lot of decimal places. If, after doing the math, you want to simplify the results for people who want to see results like 3.14 rather than 3.14159265, well you can do that with DecimalFormat, or with a format string like "%5.2f". and you can do that without pointlessly losing data from the actual calculations. Displaying simplified data is a presentation issue - don't mangle your data for it.

And lastly, if you want to write tests verifying the accuracy of results using double, you need to look at the actual API for the test framework you're using. Expecting "exact" equality of calculations is a fool's errand to begin with, so the tester needs to adjust expectations. But JUnit does handle this sort of thing in their API. Hint: the signatures to look for are

or

 
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic