I'm using the DecimalFormat with HALF_UP rounding mode and I have an escenery where is not working correctly and I don't know why.
DecimalFormat df = new DecimalFormat("#.##");
df.setRoundingMode(RoundingMode.HALF_UP);
float tmp = (float) (0.5 * 1.05);
df.format(tmp);
float mul = Float.parseFloat(df.format(tmp));
The mul variable value I hope have 0.53 value and I received 0.52 value.
I'm using the Java 1.8.0_131.
SOLVED FINAL CODE
BigDecimal mul = new BigDecimal(0.5).multiply(new igDecimal(1.05));
mul = mul.setScale(2, RoundingMode.HALF_UP);
System.out.println(mul);
You are using the float datatype.
This datatype is not able to precisely hold the value 0.525. See this code for making it clear:
float value = (float) (0.5 * 1.05);
DecimalFormat df = new DecimalFormat("#.########################");
System.out.println(df.format(value));
This prints out:
0.5249999761581421
Rounding such a value with the mode RoundingMode.HALF_UP will correctly yield 0.52.
The double value seems to be able to precisely store the value 0.525:
double value = 0.5 * 1.05;
DecimalFormat df = new DecimalFormat("#.########################");
System.out.println(df.format(value));
This will print the expected value:
0.525
Rounding that value with the mode RoundingMode.HALF_UP will now yield 0.53!
Caution: Even the double datatype does not store the value precisely!
Look at #MarkDickinson's comment. The stored value is 0.52500000000000002220446049250313080847263336181640625 which happens to be larger than 0.525 and only rounds by accident to the expected value.
So what to do?
The data types float and double are binary-based, whereas we humans tend to think decimal-based when dealing with numbers. Read the article "What Every Computer Scientist Should Know About Floating-Point Arithmetic" for much more information.
The solution is to use a decimal-based data type, which exists in BigDecimal.
Related
I've been trying to sum up decimal values using double in java and it doesn't work well, got a wrong answer.
I've already tried with Double, Float, BigDecimal.
{
double a = 2595.00;
double b = -1760.76;
double c = -834.00;
double d = -.24;
System.out.print(a+b+c+d);
}
The expected result should be "0" But Got 9.1038288019262836314737796783447265625E-15
You can use BigDecimal for this purpose and make sure you input the numbers as String in the BigDecimal constructor:
BigDecimal a = new BigDecimal("2595.00");
BigDecimal b = new BigDecimal("-1760.76");
BigDecimal c = new BigDecimal("-834.00");
BigDecimal d = new BigDecimal("-.24");
System.out.println(a.add(b).add(c).add(d));
Live Example
Output is:
0.00
From the Java docs for BigDecimal(String):
This is generally the preferred way to convert a float or double into
a BigDecimal, as it doesn't suffer from the unpredictability of the
BigDecimal(double) constructor.
Check this SO thread for why double results in a loss of precision.
As already pointed by the previous answers about double precision, the value here is very close to zero. You can see it with System.out.format as well.
System.out.format("%.14f%n",a+b+c+d);
System.out.format("%1.1f%n",a+b+c+d); //to print 0.0
I've found that using the decimal format and rounding mode shown behaves unexpectedly with some values
double b = 123.135;
//double b = 1896.675;
//double b = 523.135;
DecimalFormat df = new DecimalFormat(".##");
df.setRoundingMode(RoundingMode.HALF_UP);
System.out.println(b);
String a = df.format(b);
System.out.println(a);
double roundOff = Math.round(b * 100.0) / 100.0;
System.out.println(roundOff);
Produces:
123.135
123.14
123.14
Which I believe to be correct.
While using this value: 1896.675 produces the following:
1896.675
1896.67
1896.68
Which I regard as unexpected - What am I doing wrong here?
Problem here is that it is not possible to store all possible fractions in a variable because of the limitations of the binary format. So basically double just approximates the value you entered and thats why rounding errors occure.
You can read more about this topic in for example this thread: how does java.math.RoundingMode work?
Long story short, if you want precise rounding use BigDecimal (and the valueOf Function of the BigDecimal class)
I want to substract 2 double values, and I have tried the following code.
double val1 = 2.0;
double val2 = 1.10;
System.out.println(val1 - val2);
and I got the output as,
0.8999999999999999
For getting output as 0.9 I tried with BigDecimal as follows,
BigDecimal val1BD = new BigDecimal(val1);
BigDecimal val2BD = new BigDecimal(val2);
System.out.println(val1BD.subtract(val2BD));
And I got the output as,
0.899999999999999911182158029987476766109466552734375
Then I tried with BigDecimal.valueOf()
val1BD = BigDecimal.valueOf(val1);
val2BD = BigDecimal.valueOf(val2);
System.out.println(val1BD.subtract(val2BD));
And finally I got the output as 0.9.
My question is what is the difference between case 2 & case 3?
In case 2 why I got the output like that?
BigDecimal.valueOf(double d) uses canonical String representation of double value, internally Double.toString(double) is used, that's why you are getting 0.9 in second case.
Note: This is generally the preferred way to convert a double (or
float) into a BigDecimal, as the value returned is equal to that
resulting from constructing a BigDecimal from the result of using
Double.toString(double).
While with new BigDecimal(0.9) it converts value to exact floating point representation of double value without using String representation,
Translates a double into a BigDecimal which is the exact decimal
representation of the double's binary floating-point value.
...
NOTES :
The results of this constructor can be somewhat unpredictable.
...
FOR EXAMPLE :
BigDecimal bd1 = new BigDecimal(Double.toString(0.9));
BigDecimal bd2 = new BigDecimal(0.9);
System.out.println(bd1);
System.out.println(bd2);
OUTPUT :
0.9
0.90000000000000002220446049250313080847263336181640625
Just for those others that got here looking for some other issue with BigDecimal(not related to the question above)...
remember to give a mathContext to the methods to avoid certain problems e.g.
MathContext mc = new MathContext(10, RoundingMode.HALF_UP);
BigDecimal hitRate = new BigDecimal(totalGetValuesHitted).divide(new BigDecimal(totalGetValuesRequested), mc);
BigDecimal missRate = new BigDecimal(1.0, mc).subtract(hitRate, mc);
I have below code:
Double a = new Double((123456798/1000000)); //123456798 this value comes from client side as a `int`
DecimalFormat df = new DecimalFormat("###.###");
log.info("a :"+a+" df "+df.format(a.doubleValue()));
output:
a :123.0 df 123
//i want output like this, a :123.xxx fd 123.xxx
please help
UPDATE:
123456798 this value comes from client side as a int so i cant do it as 123456798.0 (or something)
123456798 and 1000000 are int literals, so dividing them will use integer arithmetic, and yield 123.
Instead, you could use floating point literals in order to use floating point arithmetic:
Double a = new Double((123456798.0/1000000.0));
DecimalFormat df = new DecimalFormat("###.###");
log.info("a :"+a+" df "+df.format(a.doubleValue()));
Any one value in the division should be float or double.
Double a = new Double((123456798.0/1000000));
or
Double a = new Double((123456798/1000000.0));
if you are getting these values in variables, then multiply it with 1.0
like
Double a = new Double((variable*1.0/1000000));
Put it like that
Double a = new Double((123456798.0/1000000.0)); // <- note ".0"
the reason of the misbehavior is the integer division:
123456798/1000000
is the integer value, while
123456798.0/1000000.0
is the floating point one (double)
Double a = new Double((123456798/1000000));
You are doing integer division here. Make one of the constants a double, so that floating-point division is done. Also, why are you using Double? It's better to use the primitive type double.
double a = 123456798.0 / 1000000;
Or simply, since they are constants:
double a = 123.456789;
You perform an integer division, thats why a is incorrect:
Double a = new Double(123456798.0/1000000);
I have google on how to get 2 decimal for a float number in java. Below are my codes. Finally here float totalWeight = 0.1*levinWeight+0.8*lsmWeight; I get the error of possible loss of precision ? I would want to first covert the string into float and then have it to be 2 decimal places.
float levinWeight = Float.parseFloat(dataOnlyCombine[2]);
float lsmWeight = Float.parseFloat(dataOnlyCombine[3]);
DecimalFormat df = new DecimalFormat("#.##");
levinWeight = Float.valueOf(df.format(levinWeight));
lsmWeight = Float.valueOf(df.format(lsmWeight));
float totalWeight = 0.1*levinWeight+0.8*lsmWeight;
If you are concerned about precision
don't use float, it has the lowest precision of any option available. I suggest using double or BigDecimal
use operation which involve values which can be accurately represented. 0.1 * x will give you error because 0.1 cannot be represented precisely. Using x / 10.0 will have less error.
I would write something like this
double levinWeight = Double.parseDouble(dataOnlyCombine[2]);
double lsmWeight = Double.parseDouble(dataOnlyCombine[3]);
double totalWeight = (levinWeight + 8 * lsmWeight) / 10.0;
// perform rounding only at the end as appropriate.
// to round to two decimal places
double totalWeight2 = Math.round(totalWeight * 100) / 100.0;
float levinWeight = Float.parseFloat(dataOnlyCombine[2]);
float lsmWeight = Float.parseFloat(dataOnlyCombine[3]);
float totalWeight = 0.1*levinWeight+0.8*lsmWeight;
DecimalFormat df = new DecimalFormat("#.##");
String totalWeightValue = df.format(totalWeight);
If you really want to do it like that, then use BigDecimal. Those floating point classes are perfect for precision. Take a look at them:
http://voidexception.weebly.com/java-bigdecimal---dealing-with-high-precision-calculations.html
http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html
http://www.javaworld.com/article/2075315/core-java/make-cents-with-bigdecimal.html
Default IEEE 746 floating points will not suit your needs. Alternatively, you could use integers and thread them factor 100. So:
100 is equivalent to 1.00
452 is equivalent to 4.52
1 is equivalent to 0.01