Why substraction problems for 'double' variables ?

I'm a newby in the Java world and I would appreciate very much if anyone could help me by providing me a detailed explanation / solution to my problem.

I declared as double a variable and did the following substraction:

myVariable = myVariable - 0.01

several times till a condition was fulfilled.

There were possible 100 values for my variable, from 100% (meaning 1) down to 1% (meaning 0.01), using a step of 1%.

In my case integer values couldn't be used, being mandatory to use so the step of 0.01 and not the step of 1.

I initialised so the variable to 1 and was surprised to discover that after 7 substraction steps, instead of having 0.93 i got 0.92999...

It was like 0.94 -0.01 = 0.92999..., a behaviour which seems weird to me.

Why that bug (at least I see it as a bug)?

To get rid of the problem I set the variable to float.

However, I don't get a clean set of those 100 values, even though the first 2 decimals are correct when using the float, there are still digits different than zero afterwards.

Is there a clean solution for my design to get only those 100 values:

1, 0.99, 0.98, ..., 0.02, 0.01?

Thanks a lot for your help.

[1227 byte] By [jon_winea] at [2007-10-1 2:02:58]
# 1

> Is there a clean solution for my design to get only those 100 values:

1, 0.99, 0.98, ..., 0.02, 0.01?

Either

1) Use the BigDecimal class, not doubles or floats, or

2) Check the result of the subtraction; if it's sufficiently close to zero - you decide what's "sufficiently close" - then replace the result with zero. Then use the result as you normally would.

The cause:

Binary double and float values have a limited number of bits (1's and 0's) with which they represent base-10 decimals. Not every base-10 decimal number can be exactly represented (certain ones require an infinite number of bits!), so they are approximated. This is a "feature" of all binary floating point numbers. BigDecimal does it differently, base-10 decimals are exact.

Here is a pretty good explanation with lots of detail:

http://www.math.grin.edu/~stone/courses/fundamentals/IEEE-reals.html

ChuckBinga at 2007-7-8 10:32:49 > top of Java-index,Administration Tools,Sun Connection...
# 2

Your solution works perfectly fine; however, I wonder if it's not overkill? The BigDecimal class is a bit heavy. I would be inclined to use the byte data type and simply do the math from 100 to 1, then divide by 100 before displaying the result (as a float). The extra division operation I think would require a lot less overhead than maintaining an instance of BigDecimal.

But memory's cheap... it may be a matter of taste...

Chuckling,

Dylan

Dylan.Piercea at 2007-7-8 10:32:49 > top of Java-index,Administration Tools,Sun Connection...
# 3

Hello,

what you could do is, do the integer math, then run a shift of the decimal on the result. It is overkill, but there is no rounding needed. You do have 3 new objects (String (2), StringBuffer (1), Double (0, static call)) to play with. An example:

String shift = "00";

double d = 100.0;

d = d - 1.0;

String s = String.valueOf(d);

int i = s.indexOf(".");

StringBuffer sb = new StringBuffer(shift + s);

sb.deleteCharAt(i + shift.length());

sb.insert(i, ".");

double dd = Double.parseDouble(sb.toString());

kamilyonna at 2007-7-8 10:32:49 > top of Java-index,Administration Tools,Sun Connection...
# 4
read and shiver: http://docs.sun.com/source/806-3568/ncg_goldberg.html
jwentinga at 2007-7-8 10:32:49 > top of Java-index,Administration Tools,Sun Connection...
# 5

try using an intgere iterator and calculate the double. This aviod accumalting increasing round errors.

e.g.

for(int j = 100; j>0;j--) {

double d = 0.01 * j;

// do code using d

}

Peter-Lawreya at 2007-7-8 10:32:49 > top of Java-index,Administration Tools,Sun Connection...