73

I want a function to convert Bigdecimal 10.12 for 10.12345 and 10.13 for 10.12556. But no function is satisfying both conversion in same time.Please help to achieve this.

Below is what I tried.
With value 10.12345:

BigDecimal a = new BigDecimal("10.12345");

a.setScale(2, BigDecimal.ROUND_UP)
a.setScale(2, BigDecimal.ROUND_CEILING)
a.setScale(2, BigDecimal.ROUND_DOWN)
a.setScale(2, BigDecimal.ROUND_FLOOR)
a.setScale(2, BigDecimal.ROUND_HALF_DOWN)
a.setScale(2, BigDecimal.ROUND_HALF_EVEN)
a.setScale(2, BigDecimal.ROUND_HALF_UP)

Output :

10.12345::10.13
10.12345::10.13
10.12345::10.12
10.12345::10.12
10.12345::10.12
10.12345::10.12
10.12345::10.12

With value 10.12556:

BigDecimal b = new BigDecimal("10.12556");

b.setScale(2, BigDecimal.ROUND_UP)
b.setScale(2, BigDecimal.ROUND_CEILING)
b.setScale(2, BigDecimal.ROUND_DOWN)
b.setScale(2, BigDecimal.ROUND_FLOOR)
b.setScale(2, BigDecimal.ROUND_HALF_DOWN)
b.setScale(2, BigDecimal.ROUND_HALF_EVEN)
b.setScale(2, BigDecimal.ROUND_HALF_UP)

Output :

10.12556::10.13
10.12556::10.13
10.12556::10.12
10.12556::10.12
10.12556::10.12
10.12556::10.12
10.12556::10.12
Gaspar
  • 1,515
  • 13
  • 20
sunleo
  • 10,589
  • 35
  • 116
  • 196

5 Answers5

106

I think that the RoundingMode you are looking for is ROUND_HALF_EVEN. From the javadoc:

Rounding mode to round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor. Behaves as for ROUND_HALF_UP if the digit to the left of the discarded fraction is odd; behaves as for ROUND_HALF_DOWN if it's even. Note that this is the rounding mode that minimizes cumulative error when applied repeatedly over a sequence of calculations.

Here is a quick test case:

BigDecimal a = new BigDecimal("10.12345");
BigDecimal b = new BigDecimal("10.12556");

a = a.setScale(2, BigDecimal.ROUND_HALF_EVEN);
b = b.setScale(2, BigDecimal.ROUND_HALF_EVEN);

System.out.println(a);
System.out.println(b);

Correctly prints:

10.12
10.13

UPDATE:

setScale(int, int) has not been recommended since Java 1.5, when enums were first introduced, and was finally deprecated in Java 9. You should now use setScale(int, RoundingMode) e.g:

setScale(2, RoundingMode.HALF_EVEN)

Sophia Price
  • 848
  • 11
  • 17
Florent Bayle
  • 11,520
  • 4
  • 34
  • 47
  • 2
    I prefer to setScale after the new BigDecimal in the same line, but this answer helps me. new BigDecimal("10.12345").setScale(2, BigDecimal.ROUND_HALF_EVEN) – bpedroso Oct 10 '17 at 16:38
  • `setScale(int, int)` has not been recommended since Java 1.5, when enums were first introduced, and was finally deprecated in Java 9. You should now use `setScale(int, RoundingMode)` e.g: `setScale(2, RoundingMode.HALF_EVEN)` – Sophia Price Apr 20 '20 at 13:16
16

Add 0.001 first to the number and then call setScale(2, RoundingMode.ROUND_HALF_UP)

Code example:

public static void main(String[] args) {
    BigDecimal a = new BigDecimal("10.12445").add(new BigDecimal("0.001"));
    BigDecimal b = a.setScale(2, BigDecimal.ROUND_HALF_UP);
    System.out.println(b);
}
Erwin Bolwidt
  • 30,799
  • 15
  • 56
  • 79
  • a.add(new BigDecimal("0.001")).setScale(2, RoundingMode.HALF_UP) tried this is not working ? – sunleo Mar 24 '14 at 12:41
  • Not sure where the problem is for you. Try the code example I added, it prints "10.13". BTW you changed the question after I gave the answer, you originally asked to round "10.12345" to "10.12" and "10.12445" to "10.13". – Erwin Bolwidt Mar 24 '14 at 13:06
  • Your problem is probably the same as why you are printing the wrong output in your question itself (`new BigDecimal("10.12556").setScale(2, BigDecimal.ROUND_HALF_UP)` is `10.13`, not `10.12`) as your printed. As I said above, the only thing I can think of is that you assume that `setScale` changes the BigDecimal - but it does not, as they are immutable - the method returns a new BigDecimal. – Erwin Bolwidt Mar 24 '14 at 14:06
2

You may try this:

public static void main(String[] args) {
    BigDecimal a = new BigDecimal("10.12345");
    System.out.println(toPrecision(a, 2));
}

private static BigDecimal toPrecision(BigDecimal dec, int precision) {
    String plain = dec.movePointRight(precision).toPlainString();
    return new BigDecimal(plain.substring(0, plain.indexOf("."))).movePointLeft(precision);
}

OUTPUT:

10.12
Harmlezz
  • 7,972
  • 27
  • 35
2

You can call setScale(newScale, roundingMode) method three times with changing the newScale value from 4 to 3 to 2 like

First case

    BigDecimal a = new BigDecimal("10.12345");

    a = a.setScale(4, BigDecimal.ROUND_HALF_UP); 
    System.out.println("" + a); //10.1235
    a = a.setScale(3, BigDecimal.ROUND_HALF_UP); 
    System.out.println("" + a); //10.124
    a = a.setScale(2, BigDecimal.ROUND_HALF_UP);
    System.out.println("" + a); //10.12

Second case

    BigDecimal a = new BigDecimal("10.12556");

    a = a.setScale(4, BigDecimal.ROUND_HALF_UP); 
    System.out.println("" + a); //10.1256
    a = a.setScale(3, BigDecimal.ROUND_HALF_UP); 
    System.out.println("" + a); //10.126
    a = a.setScale(2, BigDecimal.ROUND_HALF_UP);
    System.out.println("" + a); //10.13
Arjit
  • 3,290
  • 1
  • 17
  • 18
1

According to the docs, setScale(int, int), has not been recommended since Java 1.5, when enums were first introduced:

The new setScale(int, RoundingMode) method should be used in preference to this legacy method.

It was finally deprecated in Java 9.

You should call setScale(2, RoundingMode.HALF_EVEN) instead. It makes error checking much simpler, since you can't pass in an undefined enum, but you can certainly pass in an integer mode which is undefined.