Double value to round up in Java
Asked Answered
D

10

39

I have a double value = 1.068879335 i want to round it up with only two decimal values like 1.07.

I tried like this

DecimalFormat df=new DecimalFormat("0.00");
String formate = df.format(value);
double finalValue = Double.parseDouble(formate) ;

this is giving me this following exception

java.lang.NumberFormatException: For input string: "1,07"
     at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1224)
     at java.lang.Double.parseDouble(Double.java:510)

can some one tell me what is wrong with my code.

finaly i need the finalValue = 1.07;

Design answered 25/1, 2011 at 17:23 Comment(1)
if you round this up, 1.085879335, 2 digit precision it will be 1.09 and 1.08 if round down. how come you want to get 1.07 out of 1.085879335?Confederacy
B
83

Note the comma in your string: "1,07". DecimalFormat uses a locale-specific separator string, while Double.parseDouble() does not. As you happen to live in a country where the decimal separator is ",", you can't parse your number back.

However, you can use the same DecimalFormat to parse it back:

DecimalFormat df=new DecimalFormat("0.00");
String formate = df.format(value); 
double finalValue = (Double)df.parse(formate) ;

But you really should do this instead:

double finalValue = Math.round( value * 100.0 ) / 100.0;

Note: As has been pointed out, you should only use floating point if you don't need a precise control over accuracy. (Financial calculations being the main example of when not to use them.)

Beulabeulah answered 25/1, 2011 at 17:34 Comment(7)
+1 for actually identifying the problem. You can also explicitly tell DecimalFormat to use the C locale.Provenience
As I pointed out in my answer, this algorithm does not generally work. And for as long as Java sticks with IEEE floating point such an algorithm does not exist.Volpe
@Volpe Given the context of the question, my answer is correct. Fair play for you thinking one step further, but should I know downvote your answer because you completely failed to identify why the code failed in the first place? Of course I shouldn't.Beulabeulah
Sorry, I did not want to offend. Just wanted to point out that even the corrected code does not work for every input. But then, you answered the original question.Volpe
@Volpe It's fine, I just thought it was unnecessary to edit my answer with this important gotcha because you explained it really well. Next time I won't be so lazy. :)Beulabeulah
@Beulabeulah thanks for this answer i am trying something very similar for this question. #33692831. When I am adding 51073+51073 i am getting 153219.0 instead of 102,146.Judon
@Beulabeulah Hi i want to round up like this, if decimal value is 453.65 it roun up to 454, how to do thatEschatology
A
20

Live @Sergey's solution but with integer division.

double value = 23.8764367843;
double rounded = (double) Math.round(value * 100) / 100;
System.out.println(value +" rounded is "+ rounded);

prints

23.8764367843 rounded is 23.88

EDIT: As Sergey points out, there should be no difference between multipling double*int and double*double and dividing double/int and double/double. I can't find an example where the result is different. However on x86/x64 and other systems there is a specific machine code instruction for mixed double,int values which I believe the JVM uses.

for (int j = 0; j < 11; j++) {
    long start = System.nanoTime();
    for (double i = 1; i < 1e6; i *= 1.0000001) {
        double rounded = (double) Math.round(i * 100) / 100;
    }
    long time = System.nanoTime() - start;
    System.out.printf("double,int operations %,d%n", time);
}
for (int j = 0; j < 11; j++) {
    long start = System.nanoTime();
    for (double i = 1; i < 1e6; i *= 1.0000001) {
        double rounded = (double) Math.round(i * 100.0) / 100.0;
    }
    long time = System.nanoTime() - start;
    System.out.printf("double,double operations %,d%n", time);
}

Prints

double,int operations 613,552,212
double,int operations 661,823,569
double,int operations 659,398,960
double,int operations 659,343,506
double,int operations 653,851,816
double,int operations 645,317,212
double,int operations 647,765,219
double,int operations 655,101,137
double,int operations 657,407,715
double,int operations 654,858,858
double,int operations 648,702,279
double,double operations 1,178,561,102
double,double operations 1,187,694,386
double,double operations 1,184,338,024
double,double operations 1,178,556,353
double,double operations 1,176,622,937
double,double operations 1,169,324,313
double,double operations 1,173,162,162
double,double operations 1,169,027,348
double,double operations 1,175,080,353
double,double operations 1,182,830,988
double,double operations 1,185,028,544
Antigen answered 25/1, 2011 at 18:30 Comment(4)
Where is the integer division? The cast operator has higher precedence, so you divide a double by an integer, but integer is promoted to double in such case. So it is essentially the same as my code. Or the same as Math.round(value * 100) / 100.0. It's just a matter of style. If you used genuine integer division, you'd get 23 as the result.Frerichs
@Sergey, In theory, you are right and perhaps my answer is JVM specific, see my edit for more details.Antigen
awesome. The JLS clearly states that integer must be promoted to double in such division, so the result must be the same for both cases in any conforming Java implementation, but I had no idea that JVM optimizes actual implementation in such way.Frerichs
you should ALWAYS explicitly set the native datatype next to your static numbers in your source code, and don't leave it up to the compiler to decide for you. do you? I hope you do not. this is important when writing high performance games.Though
F
8

The problem is that you use a localizing formatter that generates locale-specific decimal point, which is "," in your case. But Double.parseDouble() expects non-localized double literal. You could solve your problem by using a locale-specific parsing method or by changing locale of your formatter to something that uses "." as the decimal point. Or even better, avoid unnecessary formatting by using something like this:

double rounded = (double) Math.round(value * 100.0) / 100.0;
Frerichs answered 25/1, 2011 at 17:35 Comment(4)
How about this coming up with 23.8764367843 => 23.88000000002? I think a String processing is better.Tapdance
@Manidip, well, there is no way you get exactly 23.88 by parsing "23.88" as double either, as 23.88 isn't exactly representable in binary anyway. If exact representation is needed, some sort of arbitrary precision library should be used.Frerichs
You are right, but I thought we were talking about "textual" and "approximate" representation, not binary. Waldheinz and Peter answered that above. DecimalFormat is a good choice, but in case it doesn't work, I'd try a String roundedStr = String.valueOf (rounded); int dotPoint = roundedStr.indexOf("."); return (roundedStr).substring(0,dotPoint < 0 ? roundedStr.length() : dotPoint+2); This is not totally correct, you might have to add padding 0's to the right if there is only 1 digit to the right of the decimal point in roundedStr - but you get the idea what I was commenting about earlier.Tapdance
@Manidip, if a textual representation is needed, then it's another story of course. But then there would be no need to parse it back I think. In the end it depends on how the value will be used, of course.Frerichs
A
4

There is something fundamentally wrong with what you're trying to do. Binary floating-points values do not have decimal places. You cannot meaningfully round one to a given number of decimal places, because most "round" decimal values simply cannot be represented as a binary fraction. Which is why one should never use float or double to represent money.

So if you want decimal places in your result, that result must either be a String (which you already got with the DecimalFormat), or a BigDecimal (which has a setScale() method that does exactly what you want). Otherwise, the result cannot be what you want it to be.

Read The Floating-Point Guide for more information.

Amaya answered 25/1, 2011 at 17:37 Comment(2)
Could we imagine that he had asked for "the floating point number closest to the value rounded to two decimal places"?Provenience
@DJClayworth: where could that possibly be useful?Amaya
A
3

Try this: org.apache.commons.math3.util.Precision.round(double x, int scale)

See: http://commons.apache.org/proper/commons-math/apidocs/org/apache/commons/math3/util/Precision.html

Apache Commons Mathematics Library homepage is: http://commons.apache.org/proper/commons-math/index.html

The internal implemetation of this method is:

public static double round(double x, int scale) {
    return round(x, scale, BigDecimal.ROUND_HALF_UP);
}

public static double round(double x, int scale, int roundingMethod) {
    try {
        return (new BigDecimal
               (Double.toString(x))
               .setScale(scale, roundingMethod))
               .doubleValue();
    } catch (NumberFormatException ex) {
        if (Double.isInfinite(x)) {
            return x;
        } else {
            return Double.NaN;
        }
    }
}
Avocation answered 20/2, 2014 at 5:0 Comment(0)
T
1

You could try defining a new DecimalFormat and using it as a Double result to a new double variable.

Example given to make you understand what I just said.

double decimalnumber = 100.2397;
DecimalFormat dnf = new DecimalFormat( "#,###,###,##0.00" );
double roundednumber = new Double(dnf.format(decimalnumber)).doubleValue();
Titrate answered 25/1, 2011 at 17:30 Comment(0)
V
1

This is not possible in the requested way because there are numbers with two decimal places which can not be expressed exactly using IEEE floating point numbers (for example 1/10 = 0.1 can not be expressed as a Double or Float). The formatting should always happen as the last step before presenting the result to the user.

I guess you are asking because you want to deal with monetary values. There is no way to do this reliably with floating-point numbers, you shoud consider switching to fixed-point arithmetics. This probably means doing all calculations in "cents" instead of "dollars".

Volpe answered 25/1, 2011 at 17:35 Comment(5)
Good point, if this is really a monetary calculation then floating point is altogether the wrong data type.Beulabeulah
Could you please explain the examle? Why can't you represent 1/10 as 0.10?Tapdance
1/10 is 0.10 but floating point is binary, there is no such thing as 1/10 or 0.1. You only have 1/2, 1/4, 1/8 etc and any combination of these to approximate 0.1Antigen
@Manidip Sorry, I was AFK so I could not answer this in a timely manner. But good Peter jumped in. :-)Volpe
Ahhh, OK, you are talking about precision, I get it now.Tapdance
T
1
double TotalPrice=90.98989898898;

  DecimalFormat format_2Places = new DecimalFormat("0.00");

    TotalPrice = Double.valueOf(format_2Places.format(TotalPrice));
Tiebold answered 11/9, 2013 at 13:12 Comment(0)
D
0

You can use format like here,

  public static double getDoubleValue(String value,int digit){
    if(value==null){
        value="0";
     }
    double i=0;
     try {
         DecimalFormat digitformat = new DecimalFormat("#.##");
         digitformat.setMaximumFractionDigits(digit);
        return Double.valueOf(digitformat.format(Double.parseDouble(value)));

    } catch (NumberFormatException numberFormatExp) {
        return i;   
    }
}
Daukas answered 18/11, 2013 at 12:8 Comment(0)
S
0

If you do not want to use DecimalFormat (e.g. due to its efficiency) and you want a general solution, you could also try this method that uses scaled rounding:

public static double roundToDigits(double value, int digitCount) {
    if (digitCount < 0)
        throw new IllegalArgumentException("Digit count must be positive for rounding!");

    double factor = Math.pow(10, digitCount);
    return (double)(Math.round(value * factor)) / factor;
}
Syrian answered 9/4, 2019 at 4:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.