开发者

Why would a variable of type double have an unexpected result?

My sanity check fails because a double variable does not contain the expected result, it's really bizarre.

double a = 1117.54 + 8561.64 + 13197.37;
double b = 22876.55;
Console.WriteLine("{0} == {1}: {2}", a, b, a == b);

Gives us this output:

22876.55 == 22876.55: False

Further inspection shows us that variable a, in fact, contains the开发者_如何学编程 value 22876.550000000003.

This is reproducible in vb.net as well. Am I sane? What is going on?


Floating point types are not always capable of accurately representing their exact decimal values. It's either a "known bug" or "by design", depending on your perspective. Either way, it's a consequence of the internal representation of floating point types, and a common source of bugs.

The problem is nearly unavoidable, too, short of writing a complex computer algebra system that represents values symbolically, rather than as numeric types. Open up Windows Calculator, determine the square root of 4, and then subtract 2 from that value. You'll get some nonsensical floating point number that is incredibly close to 0, but not exactly 0. The result of your square root computation wasn't stored as exactly 2, so when you subtract exactly 2 from it, you get an "unexpected" result. Unexpected, that is, unless you know the dirty little secret about base 2 arithmetic.

If you're curious, there are several places you might go to find out more information about why this is the case. Jon Skeet wrote an article explaining binary floating point operations in the context of the .NET Framework. If you have the time, you should also peruse the aptly-named publication, What Every Computer Scientist Should Know About Floating-Point Arithmetic.

But the bottom line is that you shouldn't expect to be able to compare the result of a floating point operation to a floating point literal. In this specific case, you might try using the decimal type, instead. It's not really a "solution" (see the other answers for those, scary mathematical concepts like epsilons), but the results are often more predictable, as the decimal type is better at accurately representing base 10 numbers (such as those used in currency and financial calculations).


That's floating point rounding and this is by design - you should never expect a floating point variable to be precisely equal to any other floating point variable except a set of special cases that are quite rare.

Also see this question.


As sharptooth said, it is how variables with floating point are saved in memory. You can read more here

Also to check if two floating numbers are equal you can use something like this:

double a = 1117.54 + 8561.64 + 13197.37;
double b = 22876.55;
Console.WriteLine("{0} == {1}: {2}", a, b, fabs(a-b) < 1e-9);

What this does it checks by how much different are these numbers. If their difference is only after 9th digit after point, you can presume they are equal. To get more precision just use lower epsilon (maximum difference between 2 numbers for them to be considered equal).


You're sane. You're just dealing with numbers that can't be stored perfectly in an arbitrary number of binary digits. You'll see this in any language - the rounding "errors" are inherent in the floating point format, and thus in the hardware, too.

If you really need perfect comparisons, try using the decimal trick: Pick the smallest fraction you'll be dealing with, and express everything in terms of that. Your own personal Planck's constant, if you will. Your example code, for instance, would become:

int a = 111754 + 856164 + 1319737;
int b = 2287655;
//Convert back to decimal format for human consumption:
Console.WriteLine("{0} == {1}: {2}", ((double)a)/100, ((double)b)/100, a == b);

Hope this helps!


Floating point stores an approximation to the number (about 6-7 decimal places of accuracy for a float).

When you calculate with fp numbers you therefore often end up with tiny representational errors in each number which are carried into the calculation. Operations such as multiplying then magnify those errors. If you are not careful, the errors can become significant.

The most common problem with this is using == to determine if two values are precisely equal, because 2.999999 and 3.00000000 are very close, but not equal. Due to the error in fp representation, it is very common to end up with numbers that are close but not equal, as you have found.

Therefore, instead of saying "is my number exactly equal to 3.0", we have to say "is my number near enough to 3.0 that I am happy with it?". We do this by testing with a tolerance value, as in: "Is my value is greater than 2.999 and smaller than 3.001". When writing the number out, you can use a format string like "{0:0.000}" to round it and remove the tiny error it is displaying.

so you can achieve what you want (to 3 decimal places accuracy) with something like:

Console.WriteLine("{0:0.000} == {1:0.000}: {2}", a, b, Math.Abs(a - b) < 0.0001);


Use Decimal data-type instead of Double data-type. Numeric real literal to be treated as decimal, use the suffix m or M. Without the suffix m, the number is treated as a double and generates a compiler error.

decimal a = 1117.54M + 8561.64M + 13197.37M;
decimal b = 22876.55M;
Console.WriteLine("{0} == {1}: {2}", a, b, a == b);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜