Round a double to 2 decimal places [duplicate]
Asked Answered
T

13

603

If the value is 200.3456, it should be formatted to 200.34. If it is 200, then it should be 200.00.

Tanta answered 11/5, 2010 at 6:36 Comment(6)
As Monn commented (in an answer), do you actually want 200.34 or 200.35 for 200.3456? As you accepted my answer, I guess you did want rounding (+ maybe also formatting) and not just truncating. But could you perhaps still clarify what you meant?Fatback
Obviously not an answer to your question, but anyone reading this question should seriously consider why they really need to be using a Double instead of a BigDecimal.Dubonnet
@BillK I would assume because a BigDecimal takes a BigPerformanceHit.Nauseate
This is not a duplicate. The other question wants a String as a result. This one wants a double and the solution is different.Chippy
Not a duplicate; formatting & rounding are two completely different things.Devalue
It is a duplicate, because the only solution is to lose the floating point and use a decimal radix.Drabeck
F
946

Here's an utility that rounds (instead of truncating) a double to specified number of decimal places.

For example:

round(200.3456, 2); // returns 200.35

Original version; watch out with this

public static double round(double value, int places) {
    if (places < 0) throw new IllegalArgumentException();

    long factor = (long) Math.pow(10, places);
    value = value * factor;
    long tmp = Math.round(value);
    return (double) tmp / factor;
}

This breaks down badly in corner cases with either a very high number of decimal places (e.g. round(1000.0d, 17)) or large integer part (e.g. round(90080070060.1d, 9)). Thanks to Sloin for pointing this out.

I've been using the above to round "not-too-big" doubles to 2 or 3 decimal places happily for years (for example to clean up time in seconds for logging purposes: 27.987654321987 -> 27.99). But I guess it's best to avoid it, since more reliable ways are readily available, with cleaner code too.

So, use this instead

(Adapted from this answer by Louis Wasserman and this one by Sean Owen.)

public static double round(double value, int places) {
    if (places < 0) throw new IllegalArgumentException();

    BigDecimal bd = BigDecimal.valueOf(value);
    bd = bd.setScale(places, RoundingMode.HALF_UP);
    return bd.doubleValue();
}

Note that HALF_UP is the rounding mode "commonly taught at school". Peruse the RoundingMode documentation, if you suspect you need something else such as Bankers’ Rounding.

Of course, if you prefer, you can inline the above into a one-liner:
new BigDecimal(value).setScale(places, RoundingMode.HALF_UP).doubleValue()

And in every case

Always remember that floating point representations using float and double are inexact. For example, consider these expressions:

999199.1231231235 == 999199.1231231236 // true
1.03 - 0.41 // 0.6200000000000001

For exactness, you want to use BigDecimal. And while at it, use the constructor that takes a String, never the one taking double. For instance, try executing this:

System.out.println(new BigDecimal(1.03).subtract(new BigDecimal(0.41)));
System.out.println(new BigDecimal("1.03").subtract(new BigDecimal("0.41")));

Some excellent further reading on the topic:


If you wanted String formatting instead of (or in addition to) strictly rounding numbers, see the other answers.

Specifically, note that round(200, 0) returns 200.0. If you want to output "200.00", you should first round and then format the result for output (which is perfectly explained in Jesper's answer).

Fatback answered 11/5, 2010 at 7:1 Comment(23)
Once again, downvotes are more useful with a comment. (Note that the question is ambiguous and my answer makes it clear that it doesn't tackle every interpretation of the question.)Fatback
see the answer for why tmp/factor sort of thing might failVaasa
Read the first comment on that answer too. Obviously, if you're dealing with exact (e.g. monetary) values, you should not be using double in the first place. (In such case, use BigDecimal.)Fatback
Man if I do round(3.375d, 19+) it returns 1.0, it works only up to 18 places precision, so the method should throw IllegalArgumentException if places > 18Gut
@Sloin: You are right. While useful for many typical purposes, it does break down badly with high number of decimal places / large integer part. Thanks for pointing it out. I've updated the answer.Fatback
Thanks, this looks rock solid, btw this one liner more elegant : return new BigDecimal(value).setScale(places, BigDecimal.ROUND_HALF_UP).doubleValue();Gut
That mite help in rounding the double ,but i am afraid it will not properly format the value.In case u want exatly 2 digits this will not help always .For eg: '50.1' will give u 50.1 (even if u set the scale as 2).... i think instead of taking the doubleValue() ,we should be using the toString().Levigate
As it says in the end, this answer does not deal with formatting a double as string. The other answers (e.g. by Jesper) cover that.Fatback
@lisak: In what situation does rounding to 18 digits make sense? Isn't the point of rounding to create a simplified representation? OK, maybe when calculating interest on a large loan .. but hopefully anyone doing financial calculations already knows to use BigDecimal instead of double to represent their values, so wouldn't be looking for this solution. To abuse a method, and then call it shitty, is unprofessional. Nevertheless, it is good to highlight the limits of a solution.Twophase
@Fatback : it doesn't work well in case if the value is 0.5Iceni
@MahanteshMAmbi: What do you mean? round(0.5, 1) returns 0.5 and round(0.5, 0) gives 1, which is correct HALF_UP rounding.Fatback
This doesn't format, only rounds the double. Formatting is an entirely different matter altogether..Devalue
And what about NaNs and infinities? The should stay to be NaN and infinities.Ot
when you start from 100 000 000 some character like E7 , E8 , E9Mcdonald
Item 48: "Avoid float and double if exact answers are required" is completely misleading and should read "Avoid float and double if exact fixed digit notation answers are required". Otherwise the rounded version could be much worse than the full-form float ...Norvol
This technique fails for most values. See my answer in the duplicate for empirical proof.Drabeck
For value 15.0, I want to see 15.00. How do I achieve that?Sneck
Do NOT use the "Original answer" and NOT the "So, use this instead". Converting the result back to a double will never work reliably, because the result likely does not have an accurate binary representation. Also, all the answers given here are ignoring the accuracy of the original floating point values, which will inevitably lead to problems. See the "What Every Programmer Should Know About Floating-Point Arithmetic" in the answer.Landaulet
@JaimeMontoya Use the DecimalFormat class as described in other answers.Landaulet
@Landaulet Thank you. What I used was formatter.setMaximumFractionDigits(2); and then formatter.format(amount). That achieved what I needed but thank you for your other proposed solution.Sneck
I do not know how this can be ACCEPTED answer, yet how it can have so many upvotes. The 2nd (BigDecimal) solution, has very serious bug. To explain it to the mortals: if you have 1.555 double, it is something like 1.55499999999999996461734734717435134543 as BigDecimal. After that "HALF UP" 2 places makes 1.555 look like 1.55. I have what to say for the other too.Marcos
I use round(number/100)*100 in kotlin and it worked perfectly. You can use it in kotlin. it is same resultRansome
@Fatback Your solution using BigDecimal for rounding is excellent. However, it will encounter NumberFormatException if the input is, for example, NaN. To accommodate such cases, I suggest modifying your function to gracefully handle NaN and infinity. Here's the updated version: if (Double.isNaN(value) || Double.isInfinite(value)) return value;Kati
S
398

If you just want to print a double with two digits after the decimal point, use something like this:

double value = 200.3456;
System.out.printf("Value: %.2f", value);

If you want to have the result in a String instead of being printed to the console, use String.format() with the same arguments:

String result = String.format("%.2f", value);

Or use class DecimalFormat:

DecimalFormat df = new DecimalFormat("####0.00");
System.out.println("Value: " + df.format(value));
Solarism answered 11/5, 2010 at 6:49 Comment(8)
And with DecimalFormat you can also select the rounding mode; default will match the OP's example or they may want RoundingMode.DOWN.Greer
First two options give -0.00 for a number greater than -0.005 and less than 0. This might not be optimal if you display the value on a report.Tyche
@Tyche for your specific case, I think you should use 3 or more decimal places. That depends on how much is your error margin.Subminiaturize
This answer is the real answer... the others round the double, which is an entirely different operation to formatting.Devalue
what if you want to have the result in a double variable instead of a string?Gorlin
@Gorlin see the anwer above by Jonik.Solarism
not working in case of DecimalFormat df = new DecimalFormat("####0.00"); System.out.println(df.format(60.62499999999999));Cooperage
@Cooperage What do you mean by "not working"? It prints 60.62, which is the expected result.Solarism
I
153

I think this is easier:

double time = 200.3456;
DecimalFormat df = new DecimalFormat("#.##");      
time = Double.valueOf(df.format(time));

System.out.println(time); // 200.35

Note that this will actually do the rounding for you, not just formatting.

Infold answered 13/3, 2012 at 14:39 Comment(3)
Please note that this will break if the user locale is not US-en or similar. For example in Spanish, the format would be "#,##". Use Jonik's answer instead.Aerolite
this is wrong String.format("%.2f",$F{accrued_interest})Stull
@Aerolite you cant declare a double like that double time = 200,3456 with throw errors...Roxane
Y
97

The easiest way, would be to do a trick like this;

double val = ....;
val = val*100;
val = Math.round(val);
val = val /100;

if val starts at 200.3456 then it goes to 20034.56 then it gets rounded to 20035 then we divide it to get 200.34.

if you wanted to always round down we could always truncate by casting to an int:

double val = ....;
val = val*100;
val = (double)((int) val);
val = val /100;

This technique will work for most cases because for very large doubles (positive or negative) it may overflow. but if you know that your values will be in an appropriate range then this should work for you.

Yokefellow answered 11/5, 2010 at 6:43 Comment(3)
Good simple answer. I would just add that Math.Round should be Math.round. And the result from Math.Round(val); should be a cast as a double as it normally returns a long: val = (double) Math.round(val);Forelady
Perfect answer, because result of value will be calculated with comma in the other answers. For example, double value is 3.72 and if I use format() function, new double value changes 3,72 and If I wanna set this new value to double property, it will be throwed exception of NumberFormatException: For input string: "3,72". But you got this logical operation, not function. Best regards.Lindsay
It's the round method of the accepted answer.Refinery
E
72

Please use Apache commons math:

Precision.round(10.4567, 2)
Executrix answered 17/11, 2014 at 8:20 Comment(5)
This doesn't actually do anything in a pass-by-value language like Java, let alone what the OP asked for.Drabeck
@EJP why are you being so picky? all one has to do is assign the return value to a variable...Electra
Note that internally, Precision.round simply converts the Double to a BigDecimal and back again, like in Jonik's solution.Robert
I think it is a very clean solution, even though it makes you dependent on a library.Overestimate
@HendyIrawan It doesn't do what the OP asked for. It can't. No solution that stores the result in floating-point can possibly work. See my answer in the duplicate for why.Drabeck
W
61
function Double round2(Double val) {
    return new BigDecimal(val.toString()).setScale(2,RoundingMode.HALF_UP).doubleValue();
}

Note the toString()!!!!

This is because BigDecimal converts the exact binary form of the double!!!

These are the various suggested methods and their fail cases.

// Always Good!
new BigDecimal(val.toString()).setScale(2,RoundingMode.HALF_UP).doubleValue() 

Double val = 260.775d; //EXPECTED 260.78
260.77 - WRONG - new BigDecimal(val).setScale(2,RoundingMode.HALF_UP).doubleValue()

Double val = 260.775d; //EXPECTED 260.78
260.77 - TRY AGAIN - Math.round(val * 100.d) / 100.0d  

Double val = 256.025d; //EXPECTED 256.03d
256.02 - OOPS - new DecimalFormat("0.00").format(val) 
// By default use half even, works if you change mode to half_up 

Double val = 256.025d; //EXPECTED 256.03d
256.02 - FAIL - (int)(val * 100 + 0.5) / 100.0;
Wearisome answered 3/9, 2014 at 13:41 Comment(2)
Should be the accepted answer because the accepted one is such messy compared to your clean and readable answerDutton
I just changed BigDecimal(val.toString()) by BigDecimal(Double.toString(value)) to avoid error on val.toString() in case you use double instead Double.Terrain
L
37
double value= 200.3456;
DecimalFormat df = new DecimalFormat("0.00");      
System.out.println(df.format(value));
Lesalesak answered 28/9, 2013 at 18:24 Comment(0)
C
27

If you really want the same double, but rounded in the way you want you can use BigDecimal, for example

new BigDecimal(myValue).setScale(2, RoundingMode.HALF_UP).doubleValue();
Carden answered 24/10, 2012 at 18:10 Comment(2)
Does it also work for negative numbers?Butterfingers
Perfect! (And it works for negative numbers)Alcinia
G
22
double d = 28786.079999999998;
String str = String.format("%1.2f", d);
d = Double.valueOf(str);
Gastrulation answered 13/9, 2013 at 14:12 Comment(0)
A
12

Rounding a double is usually not what one wants. Instead, use String.format() to represent it in the desired format.

Apostolate answered 11/5, 2010 at 6:38 Comment(3)
I need to round a double to use it as a granularity level in further calculationsMuckraker
@odiszapc: Then you're using the wrong datatype. You need to use arbitrary-precision types if you want to be able to specify arbitrary precision.Apostolate
I get doubles from DB, it's native DB type. But then I round it special to use rounded values as a key to build matrix. Of course this key is a formatted String gotten with String.format method.Muckraker
S
12

For two rounding digits. Very simple and you are basically updating the variable instead of just display purposes which DecimalFormat does.

x = Math.floor(x * 100) / 100;

Signore answered 18/10, 2012 at 19:59 Comment(0)
H
3

In your question, it seems that you want to avoid rounding the numbers as well? I think .format() will round the numbers using half-up, afaik?
so if you want to round, 200.3456 should be 200.35 for a precision of 2. but in your case, if you just want the first 2 and then discard the rest?

You could multiply it by 100 and then cast to an int (or taking the floor of the number), before dividing by 100 again.

200.3456 * 100 = 20034.56;  
(int) 20034.56 = 20034;  
20034/100.0 = 200.34;

You might have issues with really really big numbers close to the boundary though. In which case converting to a string and substring'ing it would work just as easily.

Hydroxyl answered 11/5, 2010 at 6:49 Comment(1)
thanks for your answer. It is working fine if the value is 200.3456, but if the value is 200, then it should be 200.00Tanta
H
0
value = (int)(value * 100 + 0.5) / 100.0;
Hippocras answered 13/2, 2013 at 13:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.