Home All Groups Group Topic Archive Search About

Double Trouble - bug in double?

Author
25 Apr 2006 6:55 AM
Doug
There appears to be a bug with Double, unless I'm missing something?

The following lines of code are basically all the same. However, some of the
numbers evaluate to "true" while others evaluate to "false". I can not figure
this out.

Response.Write((Convert.ToDouble("3.170404") == 3.170404)); // is true
Response.Write((Convert.ToDouble("5.170404") == 5.170404)); // why is this
false?
Response.Write((Convert.ToDouble("6.170404") == 6.170404)); // why is this
false?
Response.Write((Convert.ToDouble("8.170404") == 8.170404)); // is true

Any ideas? This occurs in both .NET 1.x and 2.x. Is this a bug or am I
missing something?

Regards,

Doug

Author
25 Apr 2006 7:37 AM
Nick Hounsome
There's definitely some weird stuff going on:

Convert.ToDouble gives   5.1704039999999996
whereas the compiler gives a constant the exact  value 5.170404

Of course there is nothing that says that the compiler should use the same
conversion algorithm as the framework but it's not good.

Also 5.170404F is exactly 5.170404 but (double)5.170404F gives
5.1704039573669434 which is wrong as there should be no change on widening
float to double.

Show quote
"Doug" <D***@discussions.microsoft.com> wrote in message
news:8C4AEB0B-9707-452F-8B26-198D58C44690@microsoft.com...
> There appears to be a bug with Double, unless I'm missing something?
>
> The following lines of code are basically all the same. However, some of
> the
> numbers evaluate to "true" while others evaluate to "false". I can not
> figure
> this out.
>
> Response.Write((Convert.ToDouble("3.170404") == 3.170404)); // is true
> Response.Write((Convert.ToDouble("5.170404") == 5.170404)); // why is this
> false?
> Response.Write((Convert.ToDouble("6.170404") == 6.170404)); // why is this
> false?
> Response.Write((Convert.ToDouble("8.170404") == 8.170404)); // is true
>
> Any ideas? This occurs in both .NET 1.x and 2.x. Is this a bug or am I
> missing something?
>
> Regards,
>
> Doug
Author
25 Apr 2006 4:33 PM
Jon Skeet [C# MVP]
Nick Hounsome <N***@NickHounsome.Me.Uk> wrote:
> There's definitely some weird stuff going on:
>
> Convert.ToDouble gives   5.1704039999999996
> whereas the compiler gives a constant the exact  value 5.170404

No it doesn't. It can't possibly, as there's no double which is exactly
that value. The closest you can get is:

5.17040400000000044400394472177140414714813232421875

See http://www.pobox.com/~skeet/csharp/floatingpoint.html for more on
this, and a link to DoubleConverter.cs which is what I used to get the
above.

> Of course there is nothing that says that the compiler should use the same
> conversion algorithm as the framework but it's not good.
>
> Also 5.170404F is exactly 5.170404 but (double)5.170404F gives
> 5.1704039573669434 which is wrong as there should be no change on widening
> float to double.

No, 5.170404F is actually 5.170403957366943359375.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Author
26 Apr 2006 8:05 AM
Nick Hounsome
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MPG.1eb85fc86508b20d98d14a@msnews.microsoft.com...
> Nick Hounsome <N***@NickHounsome.Me.Uk> wrote:
>> There's definitely some weird stuff going on:
>>
>> Convert.ToDouble gives   5.1704039999999996
>> whereas the compiler gives a constant the exact  value 5.170404
>
> No it doesn't. It can't possibly, as there's no double which is exactly
> that value. The closest you can get is:
>
> 5.17040400000000044400394472177140414714813232421875

Obviously the value in the debugger is rounded but what seems to be
happening is that Convert.ToDouble is getting the nearest value less than
5.170404 and the compiler is using your value which is the nearest greater -
both are out by about 4 in the 16th place.

The fact remains that ideally everything should use the same conversions.

(My stuff with floats was wrong because I wasn't assigning back to a double
in both cases)
Author
26 Apr 2006 8:46 AM
Jon Skeet [C# MVP]
Nick Hounsome wrote:
> > No it doesn't. It can't possibly, as there's no double which is exactly
> > that value. The closest you can get is:
> >
> > 5.17040400000000044400394472177140414714813232421875
>
> Obviously the value in the debugger is rounded but what seems to be
> happening is that Convert.ToDouble is getting the nearest value less than
> 5.170404 and the compiler is using your value which is the nearest greater -
> both are out by about 4 in the 16th place.

But the compiler version is out by less - the compiler version is
nearer to the
correct value. It's interesting to note that the C# spec dictates how
the compiler should behave - the documentation for Convert.ToDouble
(and Double.Parse) is woefully inadequate, giving the impression that
the exact number will actually be returned when of course that can't
possibly be the case.

> The fact remains that ideally everything should use the same conversions.

Agreed (and never disputed, at least by me).

Jon
Author
25 Apr 2006 9:43 AM
vMike
Show quote
"Doug" <D***@discussions.microsoft.com> wrote in message
news:8C4AEB0B-9707-452F-8B26-198D58C44690@microsoft.com...
> There appears to be a bug with Double, unless I'm missing something?
>
> The following lines of code are basically all the same. However, some of
> the
> numbers evaluate to "true" while others evaluate to "false". I can not
> figure
> this out.
>
> Response.Write((Convert.ToDouble("3.170404") == 3.170404)); // is true
> Response.Write((Convert.ToDouble("5.170404") == 5.170404)); // why is this
> false?
> Response.Write((Convert.ToDouble("6.170404") == 6.170404)); // why is this
> false?
> Response.Write((Convert.ToDouble("8.170404") == 8.170404)); // is true
>
> Any ideas? This occurs in both .NET 1.x and 2.x. Is this a bug or am I
> missing something?
>
> Regards,
>
> Doug
Must have something to do with string to number conversions and floating
point precision. If you do
Response.Write((Convert.ToDouble(5.170404) == 5.170404)) it yields true.

This is from the sdk.

When you work with floating-point numbers (Single Data Type (Visual Basic)
and Double Data Type (Visual Basic)), keep in mind that they are stored as
binary fractions. This means they cannot hold an exact representation of any
quantity that is not a binary fraction (of the form k / (2 ^ n) where k and
n are integers). For example, 0.5 (= 1/2) and 0.3125 (= 5/16) can be held as
precise values, while 0.2 (= 1/5) and 0.3 (= 3/10) can be only
approximations.

Because of this imprecision, you cannot rely on exact results when you
operate on floating-point values. In particular, two values that are
theoretically equal might have slightly different representations.

To compare floating-point quantities
  1.. Calculate the absolute value of their difference, using the Abs method
of the Math class in the System namespace.

  2.. Determine an acceptable maximum difference, such that you can consider
the two quantities to be equal for practical purposes if their difference is
no greater.

  3.. Compare the absolute value of the difference to the acceptable
difference.
Author
25 Apr 2006 12:56 PM
Michael D. Ober
In other words, Single and Double are not "exact" values except for a very
limited set of numbers.  If you need that kind of precision, either use
Decimal, which is implemented as Binary Coded Decimal, or find a "big int"
library and scale your numbers to they are always integers.  This is not a
bug.  It is a restriction in the hardware that we will have to deal with as
long as computers use base 2 as their native numeric format.

Mike Ober.

Show quote
"vMike" <MicZhaYel.GeoZr***@noYandZ.geZwaYrrenZ.com> wrote in message
news:7bm3g.10040$MM6.5663@bignews3.bellsouth.net...
>
>
> "Doug" <D***@discussions.microsoft.com> wrote in message
> news:8C4AEB0B-9707-452F-8B26-198D58C44690@microsoft.com...
> > There appears to be a bug with Double, unless I'm missing something?
> >
> > The following lines of code are basically all the same. However, some of
> > the
> > numbers evaluate to "true" while others evaluate to "false". I can not
> > figure
> > this out.
> >
> > Response.Write((Convert.ToDouble("3.170404") == 3.170404)); // is true
> > Response.Write((Convert.ToDouble("5.170404") == 5.170404)); // why is
this
> > false?
> > Response.Write((Convert.ToDouble("6.170404") == 6.170404)); // why is
this
> > false?
> > Response.Write((Convert.ToDouble("8.170404") == 8.170404)); // is true
> >
> > Any ideas? This occurs in both .NET 1.x and 2.x. Is this a bug or am I
> > missing something?
> >
> > Regards,
> >
> > Doug
> Must have something to do with string to number conversions and floating
> point precision. If you do
> Response.Write((Convert.ToDouble(5.170404) == 5.170404)) it yields true.
>
> This is from the sdk.
>
> When you work with floating-point numbers (Single Data Type (Visual Basic)
> and Double Data Type (Visual Basic)), keep in mind that they are stored as
> binary fractions. This means they cannot hold an exact representation of
any
> quantity that is not a binary fraction (of the form k / (2 ^ n) where k
and
> n are integers). For example, 0.5 (= 1/2) and 0.3125 (= 5/16) can be held
as
> precise values, while 0.2 (= 1/5) and 0.3 (= 3/10) can be only
> approximations.
>
> Because of this imprecision, you cannot rely on exact results when you
> operate on floating-point values. In particular, two values that are
> theoretically equal might have slightly different representations.
>
> To compare floating-point quantities
>   1.. Calculate the absolute value of their difference, using the Abs
method
> of the Math class in the System namespace.
>
>   2.. Determine an acceptable maximum difference, such that you can
consider
> the two quantities to be equal for practical purposes if their difference
is
> no greater.
>
>   3.. Compare the absolute value of the difference to the acceptable
> difference.
>
>
>
Author
25 Apr 2006 4:35 PM
Jon Skeet [C# MVP]
Michael D. Ober <ober***@.alum.mit.edu.nospam> wrote:
> In other words, Single and Double are not "exact" values except for a very
> limited set of numbers.  If you need that kind of precision, either use
> Decimal, which is implemented as Binary Coded Decimal, or find a "big int"
> library and scale your numbers to they are always integers.

Decimal isn't implemented as a Binary Coded Decimal. It's a floating
point number with a base of 10.

See http://www.pobox.com/~skeet/csharp/decimal.html

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Author
25 Apr 2006 5:04 PM
Jon Skeet [C# MVP]
Doug <D***@discussions.microsoft.com> wrote:
Show quote
> There appears to be a bug with Double, unless I'm missing something?
>
> The following lines of code are basically all the same. However, some of the
> numbers evaluate to "true" while others evaluate to "false". I can not figure
> this out.
>
> Response.Write((Convert.ToDouble("3.170404") == 3.170404)); // is true
> Response.Write((Convert.ToDouble("5.170404") == 5.170404)); // why is this
> false?
> Response.Write((Convert.ToDouble("6.170404") == 6.170404)); // why is this
> false?
> Response.Write((Convert.ToDouble("8.170404") == 8.170404)); // is true
>
> Any ideas? This occurs in both .NET 1.x and 2.x. Is this a bug or am I
> missing something?

It seems that the compiler is using a slightly different algorithm to
Convert.ToDouble.

Here's a program to find out what the values actually are:

using System;

class Test
{
    static double d2;

    static void Main()
    {
        double d1 = Convert.ToDouble ("5.170404");
        d2 = 5.170404d;
        Console.WriteLine (d1==d2);
        Console.WriteLine (d1.ToString("r"));
        Console.WriteLine (d2.ToString("r"));
        Console.WriteLine (DoubleConverter.ToExactString(d1));
        Console.WriteLine (DoubleConverter.ToExactString(d2));
    }
}

(DoubleConverter.cs is linked from
http://www.pobox.com/~skeet/csharp/floatingpoint.html )

The results are:
False
5.170404
5.1704040000000004
5.1704039999999995558255250216461718082427978515625
5.17040400000000044400394472177140414714813232421875

(The "r" specifier is "round-trip" - parsing the result should always
give the same double back.)

It looks to me like the compiler has done a better job - its value is
closer to 5.170404 than the value returned by Convert.ToDouble.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

AddThis Social Bookmark Button