1

I have some business logic with arithmetic expression and their results are as follows

(10.0 * 20.58)/1.0=205.7999..98
(6.0 * 37.9)/1.0=227.3999..98
(5.0 * 50.0)/1.0=250.0
(10.0 * 37.9)/1.0=379.0

But expected results are

(10.0 * 20.58)/1.0=205.8
(6.0 * 37.9)/1.0=227.4
(5.0 * 50.0)/1.0=250.0
(10.0 * 37.9)/1.0=379.0

I am not clear why we are getting that .999..98 fraction part? Due to that my equals comparison is failing and so business logic. For few cases we are using

amt = (double)Math.round(orderAmt*100000)/100000;

But that is not possible to do the same in each and every place where we have double arithmetic expression.

I want to know why we get such results randomly and is there any possibility to round the results to 5 decimal places instead of rounding every where?

Pokuri
  • 3,072
  • 8
  • 31
  • 55
  • 2
    You are not using decimal arithmetic with `double`. I suggest informing yourself on what the `double` actually is. – Marko Topolnik Nov 15 '12 at 14:11
  • 2
    See http://stackoverflow.com/questions/1088216/whats-wrong-with-using-to-compare-floats-in-java and http://stackoverflow.com/questions/1661273/floating-point-arithmetic-not-producing-exact-results-in-java?rq=1 – Flavio Nov 15 '12 at 14:27

5 Answers5

1

You could use BigDecimal for roundoff

BigDecimal bd = new BigDecimal((10.0 * 20.58)/1.0) ;
bd = bd.setScale(4, RoundingMode.UP);

use with a static method

public static double round(double value, int digits) {
    BigDecimal bd = new BigDecimal(value);
    return bd.setScale(digits, RoundingMode.UP).doubleValue();
}

RoundingMode has :

RoundingMode.UP;
RoundingMode.DOWN;
RoundingMode.HALF_DOWN;
RoundingMode.HALF_EVEN;
vels4j
  • 11,208
  • 5
  • 38
  • 63
1

The basic problem is that

System.out.println(10.0 * 20.58);

prints

205.79999999999998

has a small rounding error due to a representation error in 20.58

You either need to

  • round the result before comparing.
  • use a comparision which allows for some error
  • use BigDecimal (which is over kill in most cases)
  • use cents instead of dollars i.e. use int or long with fixed precision.

In the last case, the same operation would read

System.out.println(10 * 2058);

prints

20580

where this is 100x the value you need as its fixed precision e.g. cents instead of dollars.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
1

With radix 10 there are some fractions who can't be expressed exactly with a finite number of digits, like for example 1/3 = 0.33333333....

It's the same with radix 2, except that the dividers that produce this kind of results are not the one we are accustomed to, and for example, for 20.58, which is 2058 / 100, it is the case.

Internally, doubles and floats are stored with bits (an not digit), so the exact value of the double or float just can't be stored in the computer's memory. Each time you perform an operation with this value, you get a small shift, because of the approximation, which becomes visible when converting back to decimal format for printing.

It's something you have to pay attention while perfoming computations where precision is important.

So you have two solutions:

  • Store all your numbers in decimal type and perform all your calculation with it. This will achieve accuracy but for the price of performance.
  • You can also keep all the calculation with double or float, and format with a fixed number of digits only for printing results.
Samuel Rossille
  • 18,940
  • 18
  • 62
  • 90
  • Nice explanation! Thanks. Where we can read about this doubles and floats are stored with bits in more detail? any links to go through? – Pokuri Nov 16 '12 at 06:09
  • I'm sorry I have no third party references ;=). I discovered that by myself while stumbling upon a similar issue. The first time I thought about this I made the computations in radix 2 manually to check my hypothesis. – Samuel Rossille Nov 16 '12 at 06:14
0

Use float instead of double

http://ideone.com/L9vwR8

System.out.println((float)((10.0f * 20.58f)/1.0f));

output

205.8

Andrea Ligios
  • 49,480
  • 26
  • 114
  • 243
0

You may want to use double with rounding as below:

    double roundingPlaces = 10.0;//use 10.0 for single decimal digit rounding
    double a1 = 10.0;
    double b1 = 20.58;
    double c1 = 1.0;
    System.out.println(Math.round((a1*b1*roundingPlaces)/c1)/roundingPlaces);

This prints 205.8.

    float fa1 = (float) 10.0;
    float fb1 = (float)20.58;
    float fc1 = (float)1.0;
    System.out.println(fa1*fb1/fc1);    

This also prints 205.8

Yogendra Singh
  • 33,927
  • 6
  • 63
  • 73