Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / General / December 2007

Tip: Looking for answers? Try searching our database.

Problem with a double

Thread view: 
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 Magazines

Get 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 ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2009 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.