Groups | Blog | Home
all groups > dotnet framework > july 2006 >

dotnet framework : Addition error in DotNet 2.0


Owen
7/14/2006 4:25:02 AM
Hi all,
Wondering if someone can help me with this:

when i'm in Visual Studio 2005 I get a wierd addition error when adding
0.1+0.2 (both as doubles)

the result that comes back is 0.30000000000000004 not 0.3 as expected. Any
ideas what's causing this or what's wrong?

A few results:
0.1+0.2 0.30000000000000004 double
0.1+0.2+0.1+0.2 0.60000000000000009 double
0.1+0.3 0.4 double
0.2+0.3 0.5 double


Cowboy (Gregory A. Beamer) - MVP
7/14/2006 5:44:02 AM
Somewhere in your code, you are using floats, not doubles.

Try the following in a console app (C#):

static void Main(string[] args)
{
double a = 0.1;
double b = 0.2;
double c = a + b;
double d = 0.1 + 0.2;
float e = 0.1f;
float f = 0.2f;
double g = e + f;


Console.WriteLine("a={0}", a);
Console.WriteLine("b={0}", b);
Console.WriteLine("c={0}", c);
Console.WriteLine("d={0}", d);
Console.WriteLine("e={0}", e);
Console.WriteLine("f={0}", f);
Console.WriteLine("g={0}", g);
Console.Read();
}


--
Gregory A. Beamer
MVP; MCP: +I, SE, SD, DBA

***************************
Think Outside the Box!
***************************


[quoted text, click to view]
Owen
7/14/2006 6:03:02 AM
Nope.
still not right. the console outputs correct but the underlying numbers are
wrong which fails the unit testing in my application.

Check out this screen shot:
http://www.bgeek.net/photos/d/3559-2/screen.jpg

Cheers
Carl Daniel [VC++ MVP]
7/14/2006 7:00:07 AM
[quoted text, click to view]

Your expectations are wrong.

The numbers 0.1 and 0.2 do not have exact representations in binary floating
point - which is what .NET uses for floats and doubles. Consequently, when
you add a number very close to 0.1 to a number very close to 0.2, the best
you can hope for is a number very close to 0.3. Four double, which you're
apparently using here, you can expect about 17 digits of accuracy.

When comparing floating point numbers for "equality", you should always
compare for equality wiithin some small range, e.g. instead of x == y, use
Math.Abs(x-y) < 0.0000000001.

When displaying floating point numbers, you should always specify how many
digits after the decimal point, according to your needs.

If you need to be able to add 0.1 to 0.2 and get exactly 0.3 (e.g. you're
doing financial calculations), then you should use the System.Decimal type
instead. It is a decimal floating point number and can represent negative
powers of 10 (e.g. 0.1) exactly. Be aware though - decimal is orders of
magnitude slower than double, so unless you really need absolute decimal
accuracy, you're probably better off using double.

You should also take a look at the paper at the following url. It goes into
the details of working with binary floating point numbers in great detail.

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

-cd

Owen
7/14/2006 7:10:02 AM
Ok that explains why it's happening. but why is it different for 2003 and
2005? the unit tests used to pass in 2003, but now fail. And it's only for
these numbers ie if i change it to (double)0.1+(double)0.3 i get 0.4 and this
passes an nunit.Assert.AreEqual test.

Cheers
Norman Yuan
7/14/2006 7:14:09 AM
That is how "double" type works. See .NET framework document. It is the same
to other language where "duoble" or similar floating type is implemented. I
have been seeing the same or similar questions appearing in NGs for
classical VB every other week or so in many years.

If the rounding precision is an issue to your app, you might want to
consider to use "decimal" type instead of "double" or "single".

[quoted text, click to view]

Larry Lard
7/14/2006 8:07:42 AM

[quoted text, click to view]

Well, when I ask the immediate window in VS2003 and VS2005

?0.1+0.2-0.3

both times it tells me

0.000000000000000055511151231257827.

I would be surprised if the details of the Framework's floating point
implementation had changed, but they would be perfectly within their
rights to do so, so long as they still met all the relevant contracts.

By the way, thanks for the example - I always used to use
0.1+0.1+0.1-0.3 evaluating to not zero as my 'pet teaching example' of
why not to use floating point types for exact calculations, but
0.1+0.2-0.3 not being zero is, I think, a little better.


--
Larry Lard
Replies to group please
Larry Lard
7/14/2006 8:12:48 AM

[quoted text, click to view]

I think you'll find that the default formatting is leading you astray.

[quoted text, click to view]

This prints 0.3, yes, but c is not 0.3. Try

Console.WriteLine("c={0}", c-0.3);

[quoted text, click to view]

Doing the addition at compile time doesn't help, either, try

Console.WriteLine("d={0}", d-0.3);

[quoted text, click to view]

It's only apparent in this example with float and not double because
double's default ToString rounds to a point which hides the
discrepancy, but float's doesn't (it has a bigger disrecpancy too).

[quoted text, click to view]

In any event, more digits of precision would just be a palliative -
better to fix the real problem, and use an exact type (as I'm sure you
are aware).

--
Larry Lard
Replies to group please
When starting a new topic, please mention which version of VB/C# you
are using
Jon Skeet [C# MVP]
7/16/2006 12:00:00 AM
[quoted text, click to view]

You should always express this sort of test with a tolerance - i.e.
specify a range of values for which the test will pass. For more
information about floating point, along with a way of finding the exact
value of a floating point number (i.e. getting rid of formatting
rounding etc) see
http://www.pobox.com/~skeet/csharp/floatingpoint.html

--
Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
Carl Daniel [VC++ MVP]
7/16/2006 7:14:56 AM
[quoted text, click to view]

The "why" is easy: you were lucky (or perhaps it's unlucky).

-cd

Carl Daniel [VC++ MVP]
7/16/2006 8:37:08 PM
[quoted text, click to view]

The decimal type is not implemented by the x87 FPU 0 or at least not
directly.

The .NET System.Decimal type is a wrapper around the OLE VAR_DECIMAL type
and is implemented by ole32.dll (IIRC). Whether the decimal support library
makes use of the x87 decimal type I do not know. If it does, it'd have to
be doing format conversion, because the x87 decimal stores the mantissa as
BCD (decimal digit per nibble), while the .NET decimal type stores the
mantissa as a 96-bit unsigned binary integer. My guess would be that it
doesn't make use of the x87 decimal type at all.

-cd

Michael D. Ober
7/17/2006 12:00:00 AM
Thanks.

Mike.

"Carl Daniel [VC++ MVP]" <cpdaniel_remove_this_and_nospam@mvps.org.nospam>
[quoted text, click to view]


Michael D. Ober
7/17/2006 12:00:00 AM
Has anyone actually tested Decimal vs. Double. The reason I ask is that
both data types are implemented in the x87 FPU hardware (built into the
80486 and later) and have about the same clock cycle requirements.

Mike Ober.

"Carl Daniel [VC++ MVP]" <cpdaniel_remove_this_and_nospam@mvps.org.nospam>
[quoted text, click to view]


AddThis Social Bookmark Button