0

I need to round numbers to two decimal places:

1.1245 → 1.13 1.1235 → 1.12

I have spent the last two days scouring StackOverflow, and I have been unable to get any solution I have found to work at all:

They all seem to simply truncate the number at the third decimal place, then round it from there, which is not what I am trying to do. I am trying to start at the place furthest to the right, and then begin rounding appropriately; 5-9 goes up, 0-4 has no effect and is dropped.

Here is the current code I am working with:

BigDecimal a = new BigDecimal("1.1245");
double b = Math.round(a.doubleValue() * 100) / 100;

System.out.print(a.setScale(2, BigDecimal.ROUND_HALF_EVEN) + " : "
+ a.setScale(2, BigDecimal.ROUND_HALF_UP) + " : "
+ b);

And here is the output straight from the console:

console output

The problem here is that I am expecting 1.13 to be the result in all of the above cases, as though the following process is followed:

1.1245 → 1.125 → 1.13
1.1235 → 1.124 → 1.12
Community
  • 1
  • 1
nihilon
  • 834
  • 2
  • 8
  • 23
  • 5
    your rounding rule is quite uncommon – njzk2 Jan 06 '15 at 18:05
  • 6
    what you are saying is that `1.1244 -> 1.12` and `1.245 -> 1.13`? (no one does that) – njzk2 Jan 06 '15 at 18:07
  • 1
    Why would this be what you want? `1.1245` is _strictly_ closer to 1.12 than it is to 1.13. – Louis Wasserman Jan 06 '15 at 18:07
  • 1
    Yeah, the bug is in the spec – Raffaele Jan 06 '15 at 18:08
  • @njzk2, quite uncommon? Since when? If you were to round a number manually, using paper and pencil, that is exactly how it would work. You would start at the furthest right place and move left accordingly. 5-9 would round the next digit left up by one, whereas 0-4 would have no affect. I do see where I mis-stated the rule, and will edit accordingly. But I am confused about how that is uncommon. – nihilon Jan 06 '15 at 18:09
  • 3
    You could make a method that does in decremental steps `a.setScale(i - 1, BigDecimal.ROUND_HALF_UP)`. _But_ that is _not_ the standard mathematical notion of rounding up. 1.1245 is rounded 1.2, as it is smaller than 1.250000. Is it a financial software scam? – Joop Eggen Jan 06 '15 at 18:10
  • @njzk2, you are wrong, at least one person you know of does. And yes, that is exactly what I am saying. – nihilon Jan 06 '15 at 18:12
  • @nihilon Did you try using the BigDecimal.ROUND_DOWN method. – TheGaME Jan 06 '15 at 18:13
  • @nihilon: I recommend you try your method on `0.45`. With your method it gives 1 (0.45, 0.5, 1), when it should give 0. – njzk2 Jan 06 '15 at 18:15
  • @JoopEggen, no I am not dealing with money. I am dealing with time intervals in seconds. In the case of this application, I would want a number like 1.1245 to be rounded to 1.13 as stated in the OP. I will give an incremental method a try. – nihilon Jan 06 '15 at 18:15
  • @njzk2, No, using the method I am describing, you should get 1. Which would be exactly what I'd be going for if I were rounding to the nearest whole, where as 0.44 would round to 0. But, going the way I would like it to go, 0.45 should not round to anything. It is already at two decimal places. – nihilon Jan 06 '15 at 18:17
  • 1
    @nihilon: Rounding is about getting the closest value. `|1.13 - 1.1245| == 0.0055 > 0.0045 == |1.12 - 1.1245|` – njzk2 Jan 06 '15 at 18:17
  • 1
    @nihilon: `using the method I am describing, you should get 1` Exactly, that is what I said. `With your method it gives 1`. And it would *not* be the `nearest whole`. – njzk2 Jan 06 '15 at 18:18
  • Check this link http://stackoverflow.com/questions/5681868/efficient-bigdecimal-round-up-and-down-to-two-decimals – TheGaME Jan 06 '15 at 18:18
  • @njzk2, I just ran 0.45 through the code I have. It returns 0.45 like I expected it to. I am not sure how you are getting 1, but it won't repeat for me. And I understand what you are saying, but that is not the rule I am trying to go with. I want 5-9 to round up and 0-4 to simply be dropped, starting at the right most digit and working it's way left. – nihilon Jan 06 '15 at 18:21
  • @nihilon: that was an example to simplify, in your case, with 2 decimals, take `0.0045` as an example and see the `0.01` result. (and that is still a strange notion of rounding). – njzk2 Jan 06 '15 at 18:23
  • @njzk2: ahhh OK. I see what you're saying. Also, thanks for setting me straight on rounding correctly. I see why it's a strange notion. – nihilon Jan 06 '15 at 18:27
  • 1
    I understand that OP rounding is weird, but if thats what he wants we should work on it – D. Ben Knoble Jan 06 '15 at 18:30
  • @BenKnoble: That always seems to be an issue with putting questions like this out there. Was my notion of how rounding should work wrong? Absolutely. But what was being asked for were ideas or solutions to achieve a specific rounding rule - not an evaluation of how common it was, how correct it was, why it was or was not correct, or whatever else can be argued. My point is that, unfortunately, that always seems to be the first sort of thing that is mentioned, and it almost always seems to derail the post. Thanks for staying focused. I appreciate it, for whatever that's worth. – nihilon Jan 06 '15 at 18:53
  • 1
    @nihilon: by challenging the question, a better understanding of it can be gained by both parts. There are many examples of questions where discussing the context of the issue yields better results than answering plainly the specific issue. – njzk2 Jan 06 '15 at 18:59
  • I am glad that you did challenge here. I may not have gotten straightened out if you had not. And I do understand the need to challenge questions. But I am of the opinion that the challenge should come after. But that is only an opinion. I have found that providing an answer to the specific issue can help develop a clarify the context in the mind of the asker, which is often not as clear as is first thought. Same thing, different approach. – nihilon Jan 06 '15 at 20:24

3 Answers3

1

I think you should double check your spec, because the requirement is uncommon. Anyway, I think this does what you (seem to) need:

public BigDecimal round(BigDecimal n, int scale) {
    if (n.scale() > scale) {
        return round(n.setScale(n.scale() - 1, RoundingMode.HALF_UP), scale);
    } else {
        return n.setScale(scale, RoundingMode.UNNECESSARY);
    }
}
Raffaele
  • 20,627
  • 6
  • 47
  • 86
  • 1
    I think the OP expects `round("1.124");` to give `1.12`. Only `1.1245` and upward are supposed to give 1.13 – njzk2 Jan 06 '15 at 18:25
  • Yeah, and I understand that now. And why it is uncommon. And mostly wrong. Fortunately, it wasn't the spec. It was my flawed understanding of how rounding works, which is why I expected the wrong result. – nihilon Jan 06 '15 at 18:37
  • I adjusted my answer to satisfy the OP requirements. This recursive rounding function is to be called as in `round(new BigDecimal("1.1245"), 2)` – Raffaele Jan 06 '15 at 23:03
1

I would test the distance with the standard rounding, then adjust and round again :

    for (String string : new String[] {"1.1245", "1.1244", "1.1235", "1.1249", "1.1299"}) {
        BigDecimal a = new BigDecimal(string);

        if (a.setScale(2, BigDecimal.ROUND_HALF_UP).subtract(a).compareTo(new BigDecimal("-0.0044")) < 0)  {
            System.out.println(string + ":" + a.add(new BigDecimal("0.001")).setScale(2, BigDecimal.ROUND_HALF_UP));
        } else {
            System.out.println(string + ":" + a.setScale(2, BigDecimal.ROUND_HALF_UP));
        }
    }

Result:

1.1245:1.13
1.1244:1.12
1.1235:1.12
1.1249:1.13
1.1299:1.13
njzk2
  • 38,969
  • 7
  • 69
  • 107
  • I think that somebody who can needs to just delete this post to keep it from confusing anyone else. Also, thanks for the answer. I am just gonna stick with ROUND_HALF_UP and call it golden. – nihilon Jan 06 '15 at 18:47
-1
public static double Rnd(double d)
{
    d *= 100;
    d += .5;
    d = (int)d;
    d /= 100;
    return d;
}

This is the best way I have learned for rounding, as the two 100s can be adjusted to round to different places

D. Ben Knoble
  • 4,273
  • 1
  • 20
  • 38