Problem with a double

J

jorgeba

Hi,

I have an amazing problem with a double in Java.

for (double i = 0; i<1; i=i+0.2)
System.out.println (i);

Output:
0
0.2
0.399999999 !!!!!!!!!!!
0.6
0.8

It is amazing! Does someone understand it?

Thank you in advance,
Jorge
 
D

Daniel Pitts

jorgeba said:
Hi,

I have an amazing problem with a double in Java.

for (double i = 0; i<1; i=i+0.2)
System.out.println (i);

Output:
0
0.2
0.399999999 !!!!!!!!!!!
0.6
0.8

It is amazing! Does someone understand it?

Thank you in advance,
Jorge
This is not specific to Java. This has to do with the way doubles are
stored in memory, and the way they are rounded before display.
 
J

Joshua Cranmer

jorgeba said:
Hi,

I have an amazing problem with a double in Java.

for (double i = 0; i<1; i=i+0.2)
System.out.println (i);

Output:
0
0.2
0.399999999 !!!!!!!!!!!
0.6
0.8

It is amazing! Does someone understand it?

Thank you in advance,
Jorge

The short answer: A computer cannot precisely represent a decimal
number. Therefore, it is forced to round off all of decimals. The
0.399999999 here is an example of where it was forced to round off.

The long answer: Java's default Double.toString(double) (how it converts
a double to a string) does some voodoo magic that prints out "0.2" when
the internal representation is equal to what 0.2 would be. Since the
decimal number has an infinite binary expansion, the computer rounds off
the number. When it adds 0.2 to that number, the round off (it appears
to be down here) is accumulated twice and the number is off, I believe,
by one ulp: the last binary digit in the representation is incorrect
(guesswork there).

Whenever one works with floating-point numbers on computers, one should
always have tolerance guards. A double has 52 bits of significance --
about 15 to 16 correct significant figures, although the last few may
have some accumulated round-off error. In a strictfp method/class, all
arithmetic is done in those 52 bits; otherwise, it may use the
computer's extended precision if available (Intel uses an 80-bit fp
number, which should give another dozen or so significant bits).

To limit the printout of these numbers, one can use:

System.out.printf("%.5d\n",i); // Java 5+

which prints the decimal digit to 5 decimal places.
 
A

Arne Vajhøj

jorgeba said:
I have an amazing problem with a double in Java.

for (double i = 0; i<1; i=i+0.2)
System.out.println (i);

Output:
0
0.2
0.399999999 !!!!!!!!!!!
0.6
0.8

It is amazing! Does someone understand it?

That is how floating point works.

Floating point is intended for measurements with a bit
of inaccuracy.

You may have 0.4 km from your home to your children's school.

But it is not really wrong to say that there is 0.399999999 km.

On the other hand if you are doing accounting, then your auditor
prefer all calculations to be exact.

Try read an introduction like:
http://www.lahey.com/float.htm

Arne
 
S

Stefan Ram

jorgeba said:
It is amazing! Does someone understand it?

Finite Sums of Dual fractions, as used by »double«, can not
represent all decimal fractions precisely. The output
sometimes is beautified, so that one can not always see this.
But one can see it in:

public class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( new java.math.BigDecimal( 0.1 ));
java.lang.System.out.println( new java.math.BigDecimal( "0.1" )); }}

0.1000000000000000055511151231257827021181583404541015625
0.1

See also

http://docs.sun.com/source/806-3568/ncg_goldberg.html
 
M

Mark Thornton

Joshua said:
A double has 52 bits of significance --
Normalised doubles have 53 bit mantissas. The first bit is always one so
they don't bother to store it.
 
P

Patricia Shanahan

jorgeba said:
Hi,

I have an amazing problem with a double in Java.

for (double i = 0; i<1; i=i+0.2)
System.out.println (i);

Output:
0
0.2
0.399999999 !!!!!!!!!!!
0.6
0.8

It is amazing! Does someone understand it?

It is indeed amazing. When I run a program containing your code:

public class DoubleRounding {
public static void main(String[] args) {
for (double i = 0; i<1; i=i+0.2)
System.out.println (i);
}
}

I get:

0.0
0.2
0.4
0.6000000000000001
0.8

which has only the degree of rounding error I would expect from double.

0.399999999, as the result of two additions of 0.2, would imply much
bigger rounding errors than you should get with double.

Are you sure the output came from exactly the code you posted?

Patricia
 
M

Michael Jung

jorgeba said:
I have an amazing problem with a double in Java.

for (double i = 0; i<1; i=i+0.2)
System.out.println (i);

Output:
0
0.2
0.399999999 !!!!!!!!!!!
0.6
0.8

It is amazing! Does someone understand it?

You have to realize that 0.2 (and most of its multiples) are not
machine (JVM) numbers in that there are represented as is. In fact,
you can construct examples like .2 + .2 != .4 in Java due to this
(this concrete example might not work, but something along the lines
can be constructed).

Printing such numbers is therefore not as trivial as it seems. See
the javadoc description of Double.toString to see how it is solved.
By carefully calculating the steps mentioned, you will recover your
output. That doesn't make it intuitive, though:)

Basically, .399999999 represents .2+.2 (whatever they represent in
java code...) when printed, while .4 represents another number close
by when printed.

Michael
 
P

Patricia Shanahan

Joshua said:
The short answer: A computer cannot precisely represent a decimal
number. Therefore, it is forced to round off all of decimals. The
0.399999999 here is an example of where it was forced to round off.

The long answer: Java's default Double.toString(double) (how it converts
a double to a string) does some voodoo magic that prints out "0.2" when
the internal representation is equal to what 0.2 would be. Since the
decimal number has an infinite binary expansion, the computer rounds off
the number. When it adds 0.2 to that number, the round off (it appears
to be down here) is accumulated twice and the number is off, I believe,
by one ulp: the last binary digit in the representation is incorrect
(guesswork there).

I still don't see how to get 0.399999999 from adding 0 + 0.2 + 0.2.

The double representation of 0.2 has at most half a unit least place
(ulp) of rounding error. Adding 0 to a number does not change it, so
that cannot increase the rounding error. Similarly, adding a number to
itself doubles it, which involves only a change in the exponent, not the
mantissa.

Given all that, I would expect at most the smallest possible rounding
error on conversion of the 0 + 0.2 + 0.2 result back to the shortest
decimal that rounds to its internal representation. With 53 bits
effective mantissa, counting the unrepresented bit, and a number whose
absolute magnitude is less than one, the rounding error should be less
than 2**(-53) or about 1e-16. A rounding error in the tenth decimal
place is over a million times too large.

Patricia
 
P

Patricia Shanahan

Patricia said:
I still don't see how to get 0.399999999 from adding 0 + 0.2 + 0.2.

The double representation of 0.2 has at most half a unit least place
(ulp) of rounding error. Adding 0 to a number does not change it, so
that cannot increase the rounding error. Similarly, adding a number to
itself doubles it, which involves only a change in the exponent, not the
mantissa.

Given all that, I would expect at most the smallest possible rounding
error on conversion of the 0 + 0.2 + 0.2 result back to the shortest
decimal that rounds to its internal representation. With 53 bits
effective mantissa, counting the unrepresented bit, and a number whose
absolute magnitude is less than one, the rounding error should be less
than 2**(-53) or about 1e-16. A rounding error in the tenth decimal
place is over a million times too large.

I've thought about this some more, and the answer should be "0.4". The
nearest double to 0.4 has to have the same mantissa as 0.2, but with
exponent one greater, the same as the result of 0 + 0.2 + 0.2. No other
value that rounds to that exponent and mantissa has a shorter decimal
representation than 0.4, so "0.4" is the correct answer.

Patricia
 
G

George Neuner

Hi,

I have an amazing problem with a double in Java.

for (double i = 0; i<1; i=i+0.2)
System.out.println (i);

Output:
0
0.2
0.399999999 !!!!!!!!!!!
0.6
0.8

It is amazing! Does someone understand it?

Thank you in advance,
Jorge

This question comes up entirely too often. Computer arithmetic is
*not* what you learned in school.

Please read:

David Goldberg, "What Every Computer Scientist Should Know About
Floating-Point Arithmetic"
http://perso.ens-lyon.fr/jean-michel.muller/goldberg.pdf

and for more information see

http://montcs.bloomu.edu/~bobmon/Information/IEEE-754.shtml

George
 
P

Patricia Shanahan

George said:
This question comes up entirely too often. Computer arithmetic is
*not* what you learned in school.

Please read:

David Goldberg, "What Every Computer Scientist Should Know About
Floating-Point Arithmetic"
http://perso.ens-lyon.fr/jean-michel.muller/goldberg.pdf

and for more information see

http://montcs.bloomu.edu/~bobmon/Information/IEEE-754.shtml

Posters appear to attribute this output to normal rounding error, but
that does not make sense. The answer in this case should be "0.4", and
in any case a tenth significant digit error is about a million times too
large for a single addition of a Java double to a number of the same
sign and similar magnitude to itself. If that output really came from
the quoted code, which I doubt, there is something else going on.

Patricia
 
E

Eric Sosman

jorgeba said:
Hi,

I have an amazing problem with a double in Java.

for (double i = 0; i<1; i=i+0.2)
System.out.println (i);

Output:
0
0.2
0.399999999 !!!!!!!!!!!
0.6
0.8

It is amazing! Does someone understand it?

No. I've tried your code on two different Java
versions on two different processor architectures, and
I get the same result Patricia Shanahan reports:
 
M

Mark Thornton

Eric said:
No. I've tried your code on two different Java
versions on two different processor architectures, and
I get the same result Patricia Shanahan reports:

I can't think of any Java implementation that I have ever used that
might be expected to produce that result. Surely no one has implemented
Java as an Excel macro ;-).
 
W

Wayne

Patricia said:
I've thought about this some more, and the answer should be "0.4". The
nearest double to 0.4 has to have the same mantissa as 0.2, but with
exponent one greater, the same as the result of 0 + 0.2 + 0.2. No other
value that rounds to that exponent and mantissa has a shorter decimal
representation than 0.4, so "0.4" is the correct answer.

Patricia

Perhaps the OP should try using the "strictfrp" modifier on the method?
Even today, not all hardware is IEEE 754 compliant.

when I tried it (On "Microsoft Windows XP, Pentium 4") I get:

===========Code===========
public class DblTest {
public static void main ( String [] args ) {
for ( double d = 0; d < 1; d=d+0.2 )
System.out.println( d );
}
}
=========Result===========
C:\Temp>java DblTest
0.0
0.2
0.4
0.6000000000000001
0.8
==========================

(The result is the same using strictfp modifier on main.)

I've been keeping a list of unexpected "gotchas" for those unfamiliar
with computer math. See http://www.hccfl.edu/pollock/Java/MathOddities.htm
for a list of the ones I have. (For no very good reason, this list
is an applet.)

-Wayne
 
A

Andrew Thompson

jorgeba wrote:
...
I have an amazing problem with a double in Java.

Only amazing if you are not used to it. ;-)
..Does someone understand it?

(Looking at some of the earlier replies) Apparently.

Since they have already explained much, I will
simply add..

<sscce>
import java.text.DecimalFormat;

class NumberTest {
public static void main(String[] args) {
DecimalFormat format = new DecimalFormat("0.0");
for (double i = 0; i<1; i+=0.2) {
System.out.println (i);
// get a 'user friendly' representation.
System.out.println ("Value:\t" + format.format(i));
}
}
}
</sscce>

<output>
0.0
Value: 0.0
0.2
Value: 0.2
0.4
Value: 0.4
0.6000000000000001
Value: 0.6
0.8
Value: 0.8
</output>

HTH
 
G

George Neuner

Posters appear to attribute this output to normal rounding error, but
that does not make sense. The answer in this case should be "0.4", and
in any case a tenth significant digit error is about a million times too
large for a single addition of a Java double to a number of the same
sign and similar magnitude to itself. If that output really came from
the quoted code, which I doubt, there is something else going on.

Patricia

The issue is two-fold: whether the fraction has a finite base 2
representation, and whether the fraction will fit within the
significand of the floating point number. If either condition is
false, the number is inexact.

0.4 is not stored as a 4 with a scale factor, it is stored as the
binary fraction: 0/2 + 1/4 + 1/8 + 0/16 + 0/32 + 1/64 + ... with the
leading zero contributors removed by scaling.

Neither 0.4 nor 0.2 have finite representations which fit into the
significand of the IEEE double format.

I'm not sure exactly why the OP is getting 0.399999 when he prints.
AFAICT, the actual value of the double should be 0.40000000000000002.
It should print as 0.4.

George
 
J

John W. Kennedy

Patricia said:
I've thought about this some more, and the answer should be "0.4". The
nearest double to 0.4 has to have the same mantissa as 0.2, but with
exponent one greater, the same as the result of 0 + 0.2 + 0.2. No other
value that rounds to that exponent and mantissa has a shorter decimal
representation than 0.4, so "0.4" is the correct answer.

Wait a minute. Didn't Java used to have a stupid print(double) and/or
Double.toString() that gave far fewer decimal digits than it ought to?
Didn't that get changed at some point, like 1.2 or 1.3?
--
John W. Kennedy
"When a man contemplates forcing his own convictions down another man's
throat, he is contemplating both an unchristian act and an act of
treason to the United States."
-- Joy Davidman, "Smoke on the Mountain"
 
P

Patricia Shanahan

George said:
The issue is two-fold: whether the fraction has a finite base 2
representation, and whether the fraction will fit within the
significand of the floating point number. If either condition is
false, the number is inexact.

0.4 is not stored as a 4 with a scale factor, it is stored as the
binary fraction: 0/2 + 1/4 + 1/8 + 0/16 + 0/32 + 1/64 + ... with the
leading zero contributors removed by scaling.

Neither 0.4 nor 0.2 have finite representations which fit into the
significand of the IEEE double format.

I'm not sure exactly why the OP is getting 0.399999 when he prints.
AFAICT, the actual value of the double should be 0.40000000000000002.
It should print as 0.4.
....

We certainly agree on the bottom line, that the final String answer
should be "0.4". I am confused about whether you are agreeing or
disagreeing with my analysis leading to that conclusion. Could you clarify?

Patricia
 
P

Patricia Shanahan

George Neuner wrote:
....
I'm not sure exactly why the OP is getting 0.399999 when he prints.
AFAICT, the actual value of the double should be 0.40000000000000002.

I get 0.40000000000000002220446049250313080847263336181640625

Patricia
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top