Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange behavior when casting decimal to double

I'm experiencing strange issue when casting decimal to double.

Following code returns true:

Math.Round(0.010000000312312m, 2) == 0.01m //true

However, when I cast this to double it returns false:

(double)Math.Round(0.010000000312312m, 2) == (double)0.01m //false

I've experienced this problem when I wanted to use Math.Pow and was forced to cast decimal to double since there is no Math.Pow overload for decimal.

Is this documented behavior? How can I avoid it when I'm forced to cast decimal to double?

Screenshot from Visual Studio:

Screenshot from Visual Studio

Casting Math.Round to double me following result:

(double)Math.Round(0.010000000312312m, 2)   0.0099999997764825821   double
(double)0.01m   0.01    double

UPDATE

Ok, I'm reproducing the issue as follows:

  1. When I run WPF application and check the output in watch just after it started I get true like on empty project.
  2. There is a part of application that sends values from the slider to the calculation algorithm. I get wrong result and I put breakpoint on the calculation method. Now, when I check the value in watch window I get false (without any modifications, I just refresh watch window).
  3. As soon as I reproduce the issue in some smaller project I will post it here.

UPDATE2

Unfortunately, I cannot reproduce the issue in smaller project. I think that Eric's answer explains why.

like image 362
empi Avatar asked Dec 20 '22 01:12

empi


1 Answers

People are reporting in the comments here that sometimes the result of the comparison is true and sometimes it is false.

Unfortunately, this is to be expected. The C# compiler, the jitter and the CPU are all permitted to perform arithmetic on doubles in more than 64 bit double precision, as they see fit. This means that sometimes the results of what looks like "the same" computation can be done in 64 bit precision in one calculation, 80 or 128 bit precision in another calculation, and the two results might differ in their last bit.

Let me make sure that you understand what I mean by "as they see fit". You can get different results for any reason whatsoever. You can get different results in debug and retail. You can get different results if you make the compiler do the computation in constants and if you make the runtime do the computation at runtime. You can get different results when the debugger is running. You can get different results in the runtime and the debugger's expression evaluator. Any reason whatsoever. Double arithmetic is inherently unreliable. This is due to the design of the floating point chip; double arithmetic on these chips cannot be made more repeatable without a considerable performance penalty.

For this and other reasons you should almost never compare two doubles for exact equality. Rather, subtract the doubles, and see if the absolute value of the difference is smaller than a reasonable bound.

Moreover, it is important that you understand why rounding a double to two decimal places is a difficult thing to do. A non-zero, finite double is a number of the form (1 + f) x 2e where f is a fraction with a denominator that is a power of two, and e is an exponent. Clearly it is not possible to represent 0.01 in that form, because there is no way to get a denominator equal to a power of ten out of a denominator equal to a power of two.

The double 0.01 is actually the binary number 1.0100011110101110000101000111101011100001010001111011 x 2-7, which in decimal is 0.01000000000000000020816681711721685132943093776702880859375. That is the closest you can possibly get to 0.01 in a double. If you need to represent exactly that value then use decimal. That's why its called decimal.

Incidentally, I have answered variations on this question many times on StackOverflow. For example:

Why differs floating-point precision in C# when separated by parantheses and when separated by statements?

Also, if you need to "take apart" a double to see what its bits are, this handy code that I whipped up a while back is quite useful. It requires that you install Solver Foundation, but that's a free download.

http://ericlippert.com/2011/02/17/looking-inside-a-double/

like image 193
Eric Lippert Avatar answered Dec 28 '22 08:12

Eric Lippert