For-loop with double in Java

3.9k views Asked by At

Problem. I have a String of double "0.4 0.3 2.1 etc". I need to loop in this list and count occurencies of each number.

I used

StringTokenizer stokens = new StringTokenizer(values);
while(stokens.hasMoreTokens()) {
    Double tempKey = new Double(stokens.nextToken());
    Integer tempCount = orderValues.get(tempKey);
    if (tempCount == null) 
        tempCount = 1;
    else 
        tempCount++;
    orderValues.put(tempKey, tempCount);
}

where values is the string and orderValues is a TreeMap.

After this I have to add to the TreeMap all missing values from 0 to max key Value with a 0 occurency. So I use

for(double i = 0; i<=orderValues.lastKey(); i+=0.1) {
    if (orderValues.get(new Double(i))==null) {
        orderValues.put(i,0);
    }
}

The problem is the iteration on the double. I need to iterate just on the first decimal of double value. This is the output.

0.0 => 0
0.1 => 0
0.2 => 0
0.30000000000000004 => 0
0.4 => 0
0.5 => 1
0.6 => 1
0.7 => 0
0.7999999999999999 => 0
0.8999999999999999 => 0
0.9999999999999999 => 0
1.0999999999999999 => 0
1.2 => 2
1.3 => 0
1.4 => 2
1.4000000000000001 => 0
etc..

And of course it's a problem (look at 1.4 and 1.4000000001). How can I prevent this?

The main problem is iterate on double by 0.1 value


How I Fixed (thanks to Roel). I've changed the for statement into

for(double i = 0.1; i<=orderValues.lastKey(); i=rounding(0.1+i)) {

and added the rounding function

private double rounding(double x) {
    BigDecimal bd = new BigDecimal(x);
    bd = bd.setScale(1, RoundingMode.HALF_UP);
    return bd.doubleValue();
}
2

There are 2 answers

0
Joel On

The lack of precision of double types is well-known. Usually, a solution is to use BigDecimal instead. However in your specific case, since you're stepping 0.1 at each iteration, you could also work with integers, and divide them by 10 when needed.

Note that you should also change the way you store the data. That is, use:

TreeMap<BigDecimal, Integer>

(or TreeMap<Integer, Integer>) instead of TreeMap<Double, Integer>

See also the documentation of BigDecimal: http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html It's not as easy to use as double, but much safer.

0
Oliver Hemsted On

You are using the primitive double and class type Double and there is a conversion going on between them and as they're both floating point you're seeing floating point imprecision.