Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Java BigDecimal still not correctly solving

I have a method with two double values being inputted. When trying to add them together I get incorrect values over a certain threshold so I started using BigDecimal.

However even with BigDecimal I still have incorrect values?

double value1 = 2789.45;
double value2 = 557.89;
System.out.println(BigDecimal.valueOf(value1 + value2));

prints

3347.3399999999997

when It should read as

3347.34

How can I do this correctly even if value1 and value2 could be higher than current scope? (they are calculated in a separate method).

Should I just use rounding?

like image 682
Adam Jarvis Avatar asked Oct 30 '15 11:10

Adam Jarvis


People also ask

How accurate is BigDecimal?

BigDecimal precision is de facto unlimited since it is based on an int array of arbitrary length. Though operations with double are much faster than with BigDecimal this data type should never be used for precise values, such as currency.

Which is more accurate BigDecimal or double?

A BigDecimal is an accurate way of expressing numbers. A Double has a reliable accuracy. Going with doubles of various magnitudes (say d1=1000.0 and d2=0.001) could occur in the 0.001 being dropped collectively when summing as the variation in magnitude is so large. With BigDecimal this would not occur.

Why use BigDecimal instead of double in Java?

BigDecimal reduces the chances of calculation errors. On double numbers, the BigDecimal class provides arithmetic, scale management, rounding, comparison, format conversion, and hashing functions. It compensates for the time complexity by handling large and small floating-point integers with exceptional precision.


2 Answers

Should I just use rounding?

NOPE, what you are experiencing is lose of precision of double sum.


You are suming the doubles first (value1 + value2) and after converting the double sum (which has lost precision) to a BigDecimal.

To avoid this use this instead:

double value1 = 2789.45;
double value2 = 557.89;
System.out.println(BigDecimal.valueOf(value1).add(BigDecimal.valueOf(value2)));

OUTPUT:

3347.34

Working IDEONE DEMO here


UPDATE

-1. Instead of using BigDecimal.valueOf, which is still fragile, you should be using BigDecimal from the beginning and create them with new BigDecimal("2789.45"), new BigDecimal("557.89"). As soon as you've used a double literal, you've introduced imprecision. BigDecimal.valueOf tries to get it back, but it doesn't always work. – Louis Wasserman

Actually I'm not totally agree with this. You cannot hardcode the values creating new decimals, what I can be agree is to read the values (if possible for the OP) in a String directly to avoid double precision lost:

String value1 = "2789.45"; 
BigDecimal one = new BigDecimal(value1);
String value2 = "557.89";
BigDecimal two = new BigDecimal(value2);
System.out.println(one.add(two));

OUTPUT:

3347.34

This will avoid the problems Louis Wasserman is noticing.

NEW WORKING DEMO

like image 135
Jordi Castilla Avatar answered Sep 23 '22 00:09

Jordi Castilla


Because the compiler will evaluate the sum first, it adds the two doubles first and hence you losing the precision, you essentially doing this

double value1 = 2789.45;
double value2 = 557.89;
double sum = value1 + value2; // precision lost here
System.out.println(BigDecimal.valueOf(sum));
like image 23
Sleiman Jneidi Avatar answered Sep 27 '22 00:09

Sleiman Jneidi