Bug in Framework on Double arithmetic...?

  • Thread starter Thread starter Lionel
  • Start date Start date
L

Lionel

Hi all,

I'm facing a strang issue doing calculation on Double values. I read
some thread on the newsgroups warning about type translations (eg
Single to Double), but here I'm doing no conversion at all.

The issue I'm facing looks so basic but I'm really lost... I'm doing
all my calculations in Double, and the results are not correct. Did I
miss something?

double a, b;
a = 4.5d;

b = a + 0.10d;
Console.WriteLine(ee == 4.6d); // returns true

b = a + 0.20d;
Console.WriteLine(ee == 4.7d); // returns true
b = a + 0.10d + 0.10d;
Console.WriteLine(ee == 4.7d); // returns false !!!


b = a + 0.30d;
Console.WriteLine(ee == 4.8d); // returns true
b = a + 0.10d + 0.10d + 0.10d;
Console.WriteLine(ee == 4.8d); // same: returns false


Many thanks in advance for your help,

Best regards,
Lionel

Aulofee supervision systems (http://www.aulofee.com)
* security and log correlation
* IT asset management and inventory
* reporting
* network cartography
 
Lionel said:
Hi all,

I'm facing a strang issue doing calculation on Double values. I read
some thread on the newsgroups warning about type translations (eg
Single to Double), but here I'm doing no conversion at all.

The issue I'm facing looks so basic but I'm really lost... I'm doing
all my calculations in Double, and the results are not correct. Did I
miss something?

double a, b;
a = 4.5d;

b = a + 0.10d;
Console.WriteLine(ee == 4.6d); // returns true
Lucky.


b = a + 0.20d;
Console.WriteLine(ee == 4.7d); // returns true

Lucky again!
b = a + 0.10d + 0.10d;
Console.WriteLine(ee == 4.7d); // returns false !!!

Not surprising.
b = a + 0.30d;
Console.WriteLine(ee == 4.8d); // returns true
b = a + 0.10d + 0.10d + 0.10d;
Console.WriteLine(ee == 4.8d); // same: returns false


Many thanks in advance for your help,

Short answer: Don't use floating point types for exact calculation. In
particular, don't ever use == with floating point types.

Slightly longer answer: If you are using quantities best represented by
floating point types (and numbers such as 0.1, 4.6, are not), and you
want to compare equality, decide on some small epsilon which for your
application can mean 'close enough'; and then say two floating point
quantites are equal if they are within epsilon of each other. eg:

static const double epsilon = 0.0001d;
static bool CloseEnough(double d1, double d2)
{
return (Math.Abs(d1 - d2) < epsilon);
}


Here's the C# for my favorite example for why floating point is
inappropriate for exact quantities:

Console.WriteLine((0.1d + 0.1d + 0.1d - 0.3d));

Try it! (First guess what you think the output will be of course...)

Long answer: Go look up floating point at wikipedia or somewhere :)
 
This is no bug. You can never assume that for example 2.0 + 0.1 == 2.1.
floating point numbers are using an approximating internal representation
thus you can never rely on exact values but just approximated ones.
 
Here's the C# for my favorite example for why floating point is
inappropriate for exact quantities:

Console.WriteLine((0.1d + 0.1d + 0.1d - 0.3d));

Try it! (First guess what you think the output will be of course...)

Hmm... despite understanding floating point reasonably well, I'd
expected that to be 0. No calculations are done in the above code at
runtime - it's all done by the compiler. I'd expect the compiler to be
smart enough to sort out constant expressions somewhat better. I must
look at what the language spec says...
 
Larry Lard said:
Hmm... despite understanding floating point reasonably well, I'd
expected that to be 0. No calculations are done in the above code at
runtime - it's all done by the compiler. I'd expect the compiler to be
smart enough to sort out constant expressions somewhat better. I must
look at what the language spec says...

Wouldn't the compiler just perform that same calculation internally,
using the exact same "roundoffs", and so ending up with the same
result as the resulting program would get it *that* performed the
calculation?

A quick test:
Console.WriteLine((0.1d + 0.1d + 0.1d - 0.3d));
double d1 = 0.1d;
double d2 = 0.3d;
Console.WriteLine((d1 + d1 + d1 - d2));
resulted in twice 5,55111512312578E-17

Hans Kesting
 
Hans Kesting said:
Wouldn't the compiler just perform that same calculation internally,
using the exact same "roundoffs", and so ending up with the same
result as the resulting program would get it *that* performed the
calculation?

Not necessarily. It appears to in this case, but I wouldn't have been
surprised to find that the compiler could work to a higher precision
(or use decimal arithmetic) in order to evaluate constant expressions.

You piqued my curiosity though, so I've checked with the spec, and
indeed:

<quote>
The compile-time evaluation of constant expressions uses the same rules
as run-time evaluation of non-constant expressions, except that where
run-time evaluation would have thrown an exception, compile-time
evaluation causes a compile-time error to occur.
</quote>

That seems to be a reasonable description of the observed behaviour.
 
Jon Skeet said:
<quote>
The compile-time evaluation of constant expressions uses the same rules
as run-time evaluation of non-constant expressions, except that where
run-time evaluation would have thrown an exception, compile-time
evaluation causes a compile-time error to occur.
</quote>

That seems to be a reasonable description of the observed behaviour.

IMO a very reasonable behavior. Compile-time evaluation should
be transparent. I'd hate to see the behavior of my program change
just because I changed a constant to a parameter with the same value.

Ole N.
 
Ole Nielsby said:
IMO a very reasonable behavior. Compile-time evaluation should
be transparent. I'd hate to see the behavior of my program change
just because I changed a constant to a parameter with the same value.

Well, there can be changes in terms of string handling, certainly. For
instance:

string x = "hello Jon";
string y = "hello "+"Jon";
string name = "Jon";
string z = "hello "+name;

x and y are guaranteed to be references to the same object. z is
guaranteed to be different.
 
This is not a .NET thing. This is universal across all languages (that
I've ever worked in) and all chipsets (that I've ever worked on).

Floating point numbers cannot be represented exactly. They are all very
accurate approximations. So, as you do arithmetic with them... even
apparently simple arithmetic, you introduce small amounts of error.

There is an entire area of computing, called Numerical Analysis, that
studies how various algorithms affect floating point error (either
accentuating it or damping it). However, at the very least, you can
never count on two floating point quantities that "should be" equal
being in fact equal. As Larry pointed out, if you want to compare
floating point quantities for "equality" you must choose a minimum
tolerance for error (aka an "epsilon") and compare using that.

This is, incidentally, why you should never use floating point (ie
float or double) to represent monetary values. You should always use
the Decimal type. I believe that Decimal is considerably less efficient
than float or double (I could be wrong), but it does make stronger
guarantees about accuracy.

double and float are generally used for mathematical calculations such
as work in engineering or the sciences, statistical analysis, etc.

decimal is more generally used in business, where you can't just "lose
a penny" here and there and get away with it.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top