Categories
decimal digits java rounding

How to round a number to n decimal places in Java

1416

What I would like is a method to convert a double to a string which rounds using the half-up method – i.e. if the decimal to be rounded is 5, it always rounds up to the next number. This is the standard method of rounding most people expect in most situations.

I also would like only significant digits to be displayed – i.e. there should not be any trailing zeroes.

I know one method of doing this is to use the String.format method:

String.format("%.5g%n", 0.912385);

returns:

0.91239

which is great, however it always displays numbers with 5 decimal places even if they are not significant:

String.format("%.5g%n", 0.912300);

returns:

0.91230

Another method is to use the DecimalFormatter:

DecimalFormat df = new DecimalFormat("#.#####");
df.format(0.912385);

returns:

0.91238

However as you can see this uses half-even rounding. That is it will round down if the previous digit is even. What I’d like is this:

0.912385 -> 0.91239
0.912300 -> 0.9123

What is the best way to achieve this in Java?

0

    856

    Use setRoundingMode, set the RoundingMode explicitly to handle your issue with the half-even round, then use the format pattern for your required output.

    Example:

    DecimalFormat df = new DecimalFormat("#.####");
    df.setRoundingMode(RoundingMode.CEILING);
    for (Number n : Arrays.asList(12, 123.12345, 0.23, 0.1, 2341234.212431324)) {
        Double d = n.doubleValue();
        System.out.println(df.format(d));
    }
    

    gives the output:

    12
    123.1235
    0.23
    0.1
    2341234.2125
    

    EDIT: The original answer does not address the accuracy of the double values. That is fine if you don’t care much whether it rounds up or down. But if you want accurate rounding, then you need to take the expected accuracy of the values into account. Floating point values have a binary representation internally. That means that a value like 2.7735 does not actually have that exact value internally. It can be slightly larger or slightly smaller. If the internal value is slightly smaller, then it will not round up to 2.7740. To remedy that situation, you need to be aware of the accuracy of the values that you are working with, and add or subtract that value before rounding. For example, when you know that your values are accurate up to 6 digits, then to round half-way values up, add that accuracy to the value:

    Double d = n.doubleValue() + 1e-6;
    

    To round down, subtract the accuracy.

    10

    • 10

      This is probably the best solution presented so far. The reason I didn’t spot this facility when I first looked at the DecimalFormat class is that it was only introduced in Java 1.6. Unfortunately I’m restricted to using 1.5 but it will be useful to know for the future.

      Oct 1, 2008 at 13:07

    • 1

      I tried this with: "#.##", rounding HALF_UP. 256.335f -> "256.33" …(example comes from comments to @asterite’s answer).

      – bigstones

      Dec 11, 2015 at 11:43

    • 6

      Please be carefull as DecimalFormat depend on your current Local configuration, you may not get a dot as a separator. I personnally prefer Asterite’s answer below

      – Gomino

      Mar 2, 2016 at 17:01

    • 1

      Also be aware that you should not expect DecimalFormat to be thread-safe. As per Java docs: Decimal formats are generally not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.

      – CGK

      Aug 10, 2016 at 3:05


    • 2

      how do i make it so that it does a proper rounding so it will not round 0.0004 to 0.001

      – user4919188

      Oct 20, 2016 at 8:00

    513

    Assuming value is a double, you can do:

    (double)Math.round(value * 100000d) / 100000d
    

    That’s for 5 digits precision. The number of zeros indicate the number of decimals.

    21

    • 79

      UPDATE: I just confirmed that doing this IS WAY faster than using DecimalFormat. I looped using DecimalFormat 200 times, and this method. DecimalFormat took 14ms to complete the 200 loops, this method took less than 1ms. As I suspected, this is faster. If you get paid by the clock cycle, this is what you should be doing. I’m surprised Chris Cudmore would even say what he said to be honest. allocating objects is always more expensive than casting primitives and using static methods (Math.round() as opposed to decimalFormat.format()).

      – Andi Jay

      Jul 10, 2012 at 14:35


    • 115

      This technique fails in over 90% of cases. -1.

      Oct 2, 2012 at 3:12


    • 27

      Indeed, this fails: Math.round(0.1 * Math.pow(10,20))/Math.pow(10,20) == 0.09223372036854775.

      Oct 3, 2012 at 18:51

    • 63

      Be very careful when using this method (or any rounding of floating points). It fails for something as simple as 265.335. The intermediate result of 265.335 * 100 (precision of 2 digits) is 26533.499999999996. This means it gets rounded down to 265.33. There simply are inherent problems when converting from floating point numbers to real decimal numbers. See EJP’s answer here at stackoverflow.com/a/12684082/144578

      Nov 14, 2013 at 13:51


    • 12

      @SebastiaanvandenBroek: Wow I never knew it was that easy to get a wrong answer. However, if one is working with non-exact numbers, one must recognize that any value is not exact. 265.335 really means 265.335 += tolerance, where tolerance depends on previous operations and range of input values. We do not know the true, exact value. At the edge values, either answer is arguably correct. If we need to be exact, we shouldn’t work in double. The fail here isn’t in converting back to double. Its in OP thinking he can rely on the incoming 265.335 as being exactly that.

      Aug 28, 2015 at 3:17


    203

    new BigDecimal(String.valueOf(double)).setScale(yourScale, BigDecimal.ROUND_HALF_UP);
    

    will get you a BigDecimal. To get the string out of it, just call that BigDecimal‘s toString method, or the toPlainString method for Java 5+ for a plain format string.

    Sample program:

    package trials;
    import java.math.BigDecimal;
    
    public class Trials {
    
        public static void main(String[] args) {
            int yourScale = 10;
            System.out.println(BigDecimal.valueOf(0.42344534534553453453-0.42324534524553453453).setScale(yourScale, BigDecimal.ROUND_HALF_UP));
        }
    

    6

    • 43

      That’s my preferred solution. Even shorter: BigDecimal.valueOf(doubleVar).setScale(yourScaleHere, BigDecimal.ROUND_HALF_UP); BigDecimal.valueOf(double val) actually calls Double.toString() under the hood 😉

      Feb 9, 2010 at 10:59

    • 4

      Nice. Don’t cut corners and use new BigDecimal(doubleVar) as you can run into issues with rounding of floating points

      – Edd

      Jan 26, 2015 at 17:57

    • 8

      @Edd, interestingly, the rounding issue occurs in the case SebastiaanvandenBroek mentions in comment to asterite’s answer. double val = 265.335;, BigDecimal.valueOf(val).setScale(decimals, BigDecimal.ROUND_HALF_UP).toPlainString(); => 265.34, but (new BigDecimal(val)).setScale(decimals, BigDecimal.ROUND_HALF_UP).toPlainString(); => 265.33.

      Aug 28, 2015 at 3:41


    • 7

      @ToolmakerSteve That’s because using new BigDecimal with the double takes the double value directly and attempts to use that to create the BigDecimal, whereas when using BigDecimal.valueOf or the tostring form parses it to a string first (a more exact representation) before the conversion.

      Aug 30, 2015 at 5:09

    • 1

      BigDecimal.ROUND_HALF_UP is deprecated since 9. You can use: RoundingMode.HALF_UP instead.

      Nov 16, 2021 at 13:48