4

I have a problem for the managemente of decimal number in java (JDK 1.4).

I have two double numbers first and second (as output of formatted String). I do a sum between fist and second and I receive a number with more decimal digits!

   final double first=198.4;//value extract by unmodifiable format method

   final double second=44701.2;//value extract by unmodifiable format method

   final double firstDifference= first+second; //I receive 44899.598 instead of 44899.6

   final double calculatedDifference=44900.1; // comparison value for the flow

    final double error=firstDifference-calculatedDifference;// I receive -0.50390605 instead 0.5

    if(Math.abs(error)<=0.5d)){
         //I must enter in this branch but the error not allows the correct flow!!!
    }
    /***
    * the flow of program is uncorrect and there's a very visible bug in business view
    */

I prefer not growing the threshold value (0.5d) because I'm not safe with similar situation (when I started coding, the specs was talking about 0.1d as comparison value). If it's the only solution, the value of 0.9d is safest value for this problem?

How I can resolve this situation? I thinked that this problem derive by the use of double variables, but with the float I have the same problem.

Some idea (having some tested code line, if possible ;))?

alepuzio
  • 1,382
  • 2
  • 28
  • 38
  • 2
    Actually, for me it enters the `if` condition! – adarshr Mar 24 '11 at 18:16
  • 1
    Your `if` clause has an extra parenthesis. Otherwise, code is perfect, and values its returning is also the same you are "expecting". – Kushal Mar 24 '11 at 18:24
  • I think the first values were for `float` ;) – Peter Lawrey Mar 24 '11 at 18:36
  • I think the mixed results for different users has something to do with the fact that the OP is using JDK 1.4 which is relatively old these days. (although I've been stuck supporting 1.3 recently at work. Ugh!) – gnomed Mar 24 '11 at 20:01

5 Answers5

3

You can get rounding error, but I don't see it here.

final double first=198.4;//value extract by unmodifiable format method
final double second=44701.2;//value extract by unmodifiable format method
final double firstDifference= first+second; //I receive 44899.6
final double calculatedDifference=44900.1; // comparison value for the flow
final double error=firstDifference-calculatedDifference;// I receive -0.5 

if(Math.abs(error)<=0.5d){
    // this branch is entered.
    System.out.println(error);
}

prints

-0.5

There are two ways to handle this more generally. You can define a rounding error such as

 private static final double ERROR = 1e-9;

 if(Math.abs(error)<=0.5d + ERROR){

OR use rounding

final double firstDifference= round(first+second, 1); // call a function to round to one decimal place.

OR use integers with fixed precision

final int first=1984;// 198.4 * 10
final int second=447012; // 44701.2 * 10
final int firstDifference= first+second;  //I receive 448996
final int calculatedDifference=449001; // comparison value for the flow
final int error=firstDifference-calculatedDifference;// I receive -5 

if(Math.abs(error)<=5){
    // this branch is entered.
    System.out.println(error);
}

OR You can use BigDecimal. This is often the preferred solution for many developers, but a last option IMHO. ;)

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • BigDecimal is suitable if you are not familar with working with `double` its better to be correct than performant and BigDecimal holds your hand more for rounding errors. However if you are familar with `double` you may find BigDecimal cumbersome both to code and to run. esp as there are often simple work arounds for rounding errors. – Peter Lawrey Mar 24 '11 at 18:35
  • Depends on the application. If you're working on actuarial applications primitives are garbage in terms of precision. If you're doing some kind of scientific/actuarial application BigDecimal is really the only solution. – avgvstvs Mar 24 '11 at 18:41
  • BigDecimal is particularly useful if you need the various rounding solutions it provided. However for prices/money using double with rounding or long/int with fixed precision values can do the job much faster and often cleaner. I *wouldn't* suggest `float` in 99% of cases. – Peter Lawrey Mar 24 '11 at 18:46
  • 1
    Great idea to use the int instead the double. The BigDecimal class gives me some problem with the rounding :( – alepuzio Mar 27 '11 at 17:20
  • `BigDecimal` is only better than `double` if you are indeed working with decimal numbers. But as soon as you divide 6 by 7, both are equally imprecise. – Roland Illig Oct 21 '11 at 07:16
  • BigDecimal can be less imprecise, however the level of accuracy BigDecimal can give is rarely needed. Even http://www.usdebtclock.org/ the US national debt (just an estimate) can be represented unambigously with rounding. – Peter Lawrey Oct 21 '11 at 07:30
3

This error will depend slightly on your version of Java (and I see you are using a slightly old one). However, regardless of Java version, for best results when you are particularly worried about decimal accuracy in java, you should use the BigDecimal class for your values.

This is what financial applications use for handling currency, and also what many industrial Java applications use when precision is essential.

EDIT: I see many valid comments that this solution comes with a slight performance hit (also depends on the number of operations you are doing in the first place). And really, if this is the only single place that you encounter an issue and you dont care about precision after it, then ya, go for a workaround. But if this happens frequently and in more than one place, or if you think you might be expanding your application in the future, I would use the safer BigDecimal.

gnomed
  • 5,483
  • 2
  • 26
  • 28
0

Double items store number in powers of 2. It is not possible to accurately represent a lot of numbers in terms of powers of 2 and so you get rounding issues. The same problem when using floats.

If you want to reduce these, use the Decimal type for the numbers, this should prove to be more accurate.

For doubles and floats, you must use the < comparator to check if 2 numbers are close enough to be considered equal.

See this for more details -> What is the most effective way for float and double comparison?

Community
  • 1
  • 1
Kurru
  • 14,180
  • 18
  • 64
  • 84
0

I've tested and on my machine everything is correct (tested in java 1.6). If I were you I would test strictfp modifier to method which has above operations:

public static strictfp void main(String[] args)

The problem can be connected with java version, OS, CPU you're using

lukastymo
  • 26,145
  • 14
  • 53
  • 66
0

I agree with Peter, I don't see that happening either. However, if it keeps happening to you and if the number of decimals is known and fixed in your usecase anyway, using "int" might be a solution, that's even faster than floating point operations. For the views you would then have to convert to floating points, of course.

Nodebody
  • 1,433
  • 11
  • 14