1

I have the following function that is used to round a double value in Java:

public static double round(double d, int decimalPlace) {

    BigDecimal bd = new BigDecimal(Double.toString(d));
    bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
    return bd.doubleValue();
}

As input, this function is receiving these values:

double d = 7.3149999999999995;
int decimalPlace = 2

But, when the function returns, the value returned is 7.31, instead of 7.32. I searched on the docs to see why the bd.SetScale is with that behavior, but with no success.

Does anybody could me explain why is this happening? Thanks a lot!!

regisxp
  • 956
  • 2
  • 10
  • 31

2 Answers2

11

Does anybody could me explain why is this happening?

It's obeying the documented behaviour :) From the docs:

If the scale is reduced by the operation, the unscaled value must be divided (rather than multiplied), and the value may be changed; in this case, the specified rounding mode is applied to the division.

And for RoundingMode.HALF_UP:

Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.

Now 7.3149999999999995 isn't equidistant to both 7.31 and 7.32 - it's closer to 7.31, so that's the result.

The difference between HALF_UP and HALF_DOWN would only be seen if the original value were exactly 7.315, i.e. half way between the two.

As an aside, to make sure you get exactly the number you'd expect to start with, I'd suggest using a String. For example:

double d = 0.1;
BigDecimal bd = new BigDecimal(d);

isn't the same as:

BigDecimal bd = new BigDecimal("0.1");

Converting from double to BigDecimal is usually a sign that you've got something wrong, and you should be using one type consistently throughout.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 2
    @Anshu: I'm hoping that anyone with a similar question in the future would search for HALF_UP or HALF_DOWN, find this and compare their situation with this one... – Jon Skeet Sep 28 '12 at 17:44
  • @Anshu thanks a lot for your explanation. The problem is that, another languages, like C#, round this same value to 7.32 and I need to maintain the same logic here... So, I tested all RoundingModes and none of then worked as I expected... – regisxp Sep 28 '12 at 17:58
  • Hey! The explanation is from Jon! – Anshu Sep 28 '12 at 17:59
  • 3
    @regisxp: Please post the code you're using in .NET... you need to work out *exactly* what's going on. Note my point about converting from `double` to `BigDecimal` being basically a bad idea... – Jon Skeet Sep 28 '12 at 18:02
  • @JonSkeet, after reading carefully, I noticed that the problem wasn´t the Rounding itself... Is the double´s intrinsic imprecision, that was widely discussed... On C#, decimal x = (decimal)7 * (decimal)(1 - 0.05) is 7.315, that when rounded HALF_UP, if 7.32. On Java, using doubles, 7d * (1 - 0.05) is 7.314999999... So, rewriting using BigDecimals (new BigDecimal(7.7d).multiply(new BigDecimal(1).subtract(new BigDecimal(0.05d))).doubleValue()) returs me the 'correct' value. Is there any advice to make the previous method, using BigDecimal, clear? Thanks a lot! – regisxp Sep 28 '12 at 18:41
  • 1
    @regisxp: The problem is the previous method took a `double`, which raises big flashing warning lights. I would back away from that decision to start with. – Jon Skeet Sep 28 '12 at 18:46
  • I would strongly agree with Jon that if you are even remotely serious about your decimal arithmetic (especially with currency) you should *NEVER* use double and *ALWAYS* use `BigDecimal`. – DuncanKinnear Oct 02 '12 at 20:07
1

ROUND_HALF_UP: Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.

In your example, the nearest neighbor is: 7.31 because 4 in 7.314 is closer to 0 than 10. To prove this, change 7.314... to 7.315....

Look at the different rounding modes to provide the behavior you want.

  • 1
    Also, you can instantiate BigDecimal as BigDecimal bd = new BigDecimal(d) instead of converting d to a string in the BigDecimal constructor. – Paul Roberts Sep 28 '12 at 17:42
  • No. If you involve doubles anywhere in `BigDecimal` instantions you will introduce [rounding issues](http://stackoverflow.com/questions/7186204/bigdecimal-to-use-new-or-valueof/9713581). – DuncanKinnear Nov 26 '12 at 20:35