Double Trouble - bug in double?

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

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
 
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.
 
Doug said:
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.
 
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.
 
Nick Hounsome said:
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.
 
Michael D. Ober said:
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
 
Doug said:
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 said:
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)
 
Nick said:
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
 
Back
Top