Java Forum / General / December 2007
Problem with a double
jorgeba - 09 Dec 2007 19:10 GMT 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
Daniel Pitts - 09 Dec 2007 19:22 GMT > Hi, > [quoted text clipped - 14 lines] > 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.
 Signature Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
Joshua Cranmer - 09 Dec 2007 19:22 GMT > Hi, > [quoted text clipped - 14 lines] > 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.
 Signature Beware of bugs in the above code; I have only proved it correct, not tried it. -- Donald E. Knuth
Mark Thornton - 09 Dec 2007 19:55 GMT > 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.
Patricia Shanahan - 09 Dec 2007 21:11 GMT >> Hi, >> [quoted text clipped - 27 lines] > 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
Patricia Shanahan - 09 Dec 2007 21:53 GMT >>> Hi, >>> [quoted text clipped - 43 lines] > 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
Wayne - 10 Dec 2007 22:50 GMT >>>> Hi, >>>> [quoted text clipped - 51 lines] > > 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
John W. Kennedy - 11 Dec 2007 18:59 GMT >>>> Hi, >>>> [quoted text clipped - 49 lines] > 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?
 Signature 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"
Arne Vajhøj - 09 Dec 2007 19:33 GMT > I have an amazing problem with a double in Java. > [quoted text clipped - 9 lines] > > 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
Stefan Ram - 09 Dec 2007 19:38 GMT >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
Patricia Shanahan - 09 Dec 2007 20:18 GMT > Hi, > [quoted text clipped - 11 lines] > > 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
Michael Jung - 09 Dec 2007 20:43 GMT > I have an amazing problem with a double in Java. > [quoted text clipped - 9 lines] > > 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
George Neuner - 10 Dec 2007 20:11 GMT >Hi, > [quoted text clipped - 14 lines] >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 -- for email reply remove "/" from address
Patricia Shanahan - 10 Dec 2007 20:32 GMT >> Hi, >> [quoted text clipped - 27 lines] > > 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
George Neuner - 11 Dec 2007 17:49 GMT >>> Hi, >>> [quoted text clipped - 36 lines] > >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 -- for email reply remove "/" from address
Patricia Shanahan - 11 Dec 2007 19:44 GMT >>>>Hi, >>>> [quoted text clipped - 52 lines] > 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
George Neuner - 13 Dec 2007 04:09 GMT >>>>>for (double i = 0; i<1; i=i+0.2) >>>>> System.out.println (i); [quoted text clipped - 50 lines] > >Patricia It wasn't clear from your post (and still isn't) whether you were referring to binary rounding in the arithmetic or decimal rounding in the print formatting. The "correct" answer of 0.4 should be due to decimal rounding because the default is to include 6 significant figures (which should be 0.40000) and to truncate trailing zeros.
I agree that either rounding doesn't explain the OP's strange results. However, I must point out that the OP did not mention what platform (CPU + JVM) he was using, and even though the Java spec requires IEEE-754 arithmetic, in fact no FPU hardware is completely 754 compliant - to achieve compliance some amount of software emulation is always necessary.
I can only speculate that the OP is using a platform that is farther from 754 compliance than that of the average Java user.
As for the rest, I was taking the opportunity to explain a bit more about floating point to make clear that floating point numbers are not like the real numbers we learned about in school. It wasn't aimed at you specifically but more for general consumption.
George -- for email reply remove "/" from address
Stefan Ram - 13 Dec 2007 04:32 GMT >the default is to include 6 significant figures public class Main { public static void main( final java.lang.String[] args ) { java.lang.System.out.println( java.lang.Math.atan2( 1.0, 1.0 )* 4.0 ); }}
3.141592653589793
Patricia Shanahan - 13 Dec 2007 04:35 GMT >>>>>> for (double i = 0; i<1; i=i+0.2) >>>>>> System.out.println (i); [quoted text clipped - 54 lines] > decimal rounding because the default is to include 6 significant > figures (which should be 0.40000) and to truncate trailing zeros. I think possibly you are confusing println with printf?
The conversion applied is equivalent to Double.toString(double). See http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Double.html#toString(double)
The basic rule is "There must be at least one digit to represent the fractional part, and beyond that as many, but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of type double."
There is nothing at all special about 6 digits for println.
> I agree that either rounding doesn't explain the OP's strange results. > However, I must point out that the OP did not mention what platform > (CPU + JVM) he was using, and even though the Java spec requires > IEEE-754 arithmetic, in fact no FPU hardware is completely 754 > compliant - to achieve compliance some amount of software emulation is > always necessary. The JLS has detailed rules for floating point arithmetic. Absent "strictfp", the rules allow for extra precision in some cases. I have yet to see Java produce lower precision than is specified. In this particular case, the hardware would have to be *very* weird to avoid getting the right answer.
Patricia
George Neuner - 13 Dec 2007 19:45 GMT >>The "correct" answer of 0.4 should be due to >> decimal rounding because the default is to include 6 significant >> figures (which should be 0.40000) and to truncate trailing zeros. > >I think possibly you are confusing println with printf? Oops ... you're right. Going back and forth between languages gets confusing sometimes.
>> I agree that either rounding doesn't explain the OP's strange results. >> However, I must point out that the OP did not mention what platform [quoted text clipped - 4 lines] > >The JLS has detailed rules for floating point arithmetic. That's true. However if you look at JavaME, you'll see that ME platforms are allowed to implement subsets of Java and even to bend rules for what they do implement.
George -- for email reply remove "/" from address
Mark Thornton - 13 Dec 2007 21:25 GMT > The JLS has detailed rules for floating point arithmetic. Absent > "strictfp", the rules allow for extra precision in some cases. I have > yet to see Java produce lower precision than is specified. In this > particular case, the hardware would have to be *very* weird to avoid > getting the right answer. Actually I don't think extra precision is allowed. What is permitted, without strictfp, is a larger exponent.
Mark Thornton
Patricia Shanahan - 13 Dec 2007 22:08 GMT >> The JLS has detailed rules for floating point arithmetic. Absent >> "strictfp", the rules allow for extra precision in some cases. I have [quoted text clipped - 4 lines] > Actually I don't think extra precision is allowed. What is permitted, > without strictfp, is a larger exponent. A wider exponent can lead to increased precision, by avoiding underflow in intermediate results.
Patricia
Patricia Shanahan - 11 Dec 2007 20:23 GMT ...
> 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
George Neuner - 13 Dec 2007 03:26 GMT >... >> I'm not sure exactly why the OP is getting 0.399999 when he prints. [quoted text clipped - 3 lines] > >Patricia IEEE double precision has only 17 significant decimal figures. Are you calculating that on a VAX?
George -- for email reply remove "/" from address
Stefan Ram - 13 Dec 2007 03:42 GMT >>I get 0.40000000000000002220446049250313080847263336181640625 >IEEE double precision has only 17 significant decimal figures. I assume, what she does is showing you the insignificant decimal figures, too.
Patricia Shanahan - 13 Dec 2007 03:43 GMT >> ... >>> I'm not sure exactly why the OP is getting 0.399999 when he prints. [quoted text clipped - 5 lines] > IEEE double precision has only 17 significant decimal figures. Are > you calculating that on a VAX? new BigDecimal(0.4).toString()
"new BigDecimal(someDouble) returns a BigDecimal whose value is exactly the value of the double. BigDecimal's toString() is also exact.
Although double has about the same precision as 16 significant decimal digit arithmetic, the doubles do not align with the short decimal numbers. Any numeric double can be exactly represented as a decimal fraction, but it often takes a lot more than 17 digits.
Patricia
George Neuner - 13 Dec 2007 19:52 GMT >>> ... >>>> I'm not sure exactly why the OP is getting 0.399999 when he prints. [quoted text clipped - 16 lines] >numbers. Any numeric double can be exactly represented as a decimal >fraction, but it often takes a lot more than 17 digits. That's cheating ;)
BigDecimals don't have the same semantics as hardware FP. If you have enough bits you can represent any real number - not just the subset also representable by IEEE double precision numbers. In any event, we were discussing fractions representable in 52 bits.
George -- for email reply remove "/" from address
Patricia Shanahan - 13 Dec 2007 20:15 GMT >>>> ... >>>>> I'm not sure exactly why the OP is getting 0.399999 when he prints. [quoted text clipped - 20 lines] > also representable by IEEE double precision numbers. In any event, we > were discussing fractions representable in 52 bits. The number I posted is the *exact* decimal representation of a number that, as a binary fraction, can be represented in 53 bits.
Think about e.g. 1/8. As a binary fraction, 0.001, it has one non-zero digit. As a decimal fraction, 0.125, it has three non-zero digits. The fact that a number can be represented with a 53 bit binary mantissa is not sufficient to ensure it can be represented in 53 decimal digits, let alone 17.
Note that normalized Java doubles really have 53 bits of mantissa, despite only having space for 52 mantissa bits. The leading digit of the mantissa is a non-zero binary digit, so storing it would be a waste of space.
It is not true that "if you have enough bits you can represent any real number". There are more real numbers than there are finite length bit sequences.
Patricia
Lew - 14 Dec 2007 02:01 GMT > It is not true that "if you have enough bits you can represent any real > number". There are more real numbers than there are finite length bit > sequences. Aleph-1 is greater than Aleph-0.
 Signature Lew
Eric Sosman - 10 Dec 2007 20:56 GMT > Hi, > [quoted text clipped - 11 lines] > > 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:
Mark Thornton - 10 Dec 2007 21:43 GMT >> Hi, >> [quoted text clipped - 15 lines] > 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 ;-).
Andrew Thompson - 11 Dec 2007 05:55 GMT ...
>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
 Signature Andrew Thompson http://www.physci.org/
Roedy Green - 12 Dec 2007 00:51 GMT On Sun, 9 Dec 2007 11:10:38 -0800 (PST), jorgeba <jorgemarcc@gmail.com> wrote, quoted or indirectly quoted someone who said :
>It is amazing! Does someone understand it? It is one of the most commonly asked questions. See http://mindprod.com/jgloss/floatingpoint.html
 Signature Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Patricia Shanahan - 12 Dec 2007 01:42 GMT > On Sun, 9 Dec 2007 11:10:38 -0800 (PST), jorgeba > <jorgemarcc@gmail.com> wrote, quoted or indirectly quoted someone who [quoted text clipped - 4 lines] > It is one of the most commonly asked questions. See > http://mindprod.com/jgloss/floatingpoint.html I don't think anyone really understands the claimed output, 0.399999999 as the result of adding ((0 + 0.2) + 0.2).
The relative error is a million times too large to be explained by double precision rounding on adding two numbers of similar magnitude and equal sign. In any case, in IEEE 754 binary floating point, 0.4 has the same representation as the result of adding the representation of 0.2 to itself, so the String answer should have been "0.4".
I am concerned about the fuzz-inserting demon model, because it causes people to accept as rounding error results that have to have a different, as yet unknown, cause.
Patricia
Roedy Green - 12 Dec 2007 16:33 GMT >The relative error is a million times too large to be explained by >double precision rounding on adding two numbers of similar magnitude and >equal sign. In any case, in IEEE 754 binary floating point, 0.4 has the >same representation as the result of adding the representation of 0.2 to >itself, so the String answer should have been "0.4". I have added a point to the entry at http://mindprod.com/jgloss/floatingpoint.html. Is this what you were referring to?
"When numbers are converted from float or double to String for display they may be truncated. Further the process of converting from binary to decimal introduces even more errors that were not in the original computation result, possibly because of repeaters -- fractions that cannot be represented exactly in binary or decimal."
 Signature Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Mark Thornton - 12 Dec 2007 19:55 GMT >> The relative error is a million times too large to be explained by >> double precision rounding on adding two numbers of similar magnitude and [quoted text clipped - 11 lines] > computation result, possibly because of repeaters -- fractions that > cannot be represented exactly in binary or decimal." No, I think that is an example what Patricia does NOT like. The conversion of floating point values to string is very precisely defined in Java. Your statement gives the impression of unbounded error which is very much not the case.
Mark Thornton
Patricia Shanahan - 12 Dec 2007 20:06 GMT >>> The relative error is a million times too large to be explained by >>> double precision rounding on adding two numbers of similar magnitude and [quoted text clipped - 16 lines] > in Java. Your statement gives the impression of unbounded error which is > very much not the case. ...
Indeed. Dismissing the anomalous result as being floating point rounding error stands in the way of finding out what is really going wrong in the OP's program. Maybe it is just a matter of confusion between two versions, in which case it does not matter. However, there could be a real bug that is being masked.
More generally, the fuzz-inserting demon model could have two bad consequences:
1. Acceptance of bugs as being due to rounding error.
2. Non-use of floating point when it would be the best choice, because of uncomprehending fear of rounding error.
Patricia
Mark Thornton - 12 Dec 2007 19:50 GMT >> On Sun, 9 Dec 2007 11:10:38 -0800 (PST), jorgeba >> <jorgemarcc@gmail.com> wrote, quoted or indirectly quoted someone who [quoted text clipped - 19 lines] > > Patricia Far too many programmers treat floating point as a no go area or "here be dragons".
Mark Thornton
Roedy Green - 12 Dec 2007 16:41 GMT On Sun, 9 Dec 2007 11:10:38 -0800 (PST), jorgeba <jorgemarcc@gmail.com> wrote, quoted or indirectly quoted someone who said :
>I have an amazing problem with a double in Java. What version of Java are you using? What hardware are you running it on?
Could you also post your complete code.
 Signature Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|