3

It is commonly known that it is not safe to compare double values with == operator. In this example it returns false:

double d1 = 0.11;
double d2 = 0.44 - 0.33;
Console.WriteLine(d1 == d2);

But, if we cast values to decimal, it returns true:

double d1 = 0.11;
double d2 = 0.44 - 0.33;
Console.WriteLine((decimal)d1 == (decimal)d2);

Is it always safe to compare decimals casted from double, or there are some cases where can give unexpected result?

Update: Hossein's example is good, it shows that it can be wrongly true. But I'm more interested to see if there are opposite examples, when we expect decimal values to be equal and they are not. Even wider, what exactly happens when we cast from double to decimal.

Alex Butenko
  • 3,664
  • 3
  • 35
  • 54
  • Possible duplicate of [to compare double and decimal should I cast double to decimal or decimal to double?](http://stackoverflow.com/questions/10612207/to-compare-double-and-decimal-should-i-cast-double-to-decimal-or-decimal-to-doub) – Alexander May 12 '17 at 04:57
  • @Alexander No, I've seen this question before asking mine. My question is completely different. – Alex Butenko May 12 '17 at 06:58
  • there is answer in that topic which describes then this conversion is possible without compromising accuracy . – Alexander May 12 '17 at 07:27
  • @Alexander But it's not what I asked about. – Alex Butenko May 12 '17 at 07:31

5 Answers5

2

It is commonly known that it is not safe to compare double values in .net. In this example it returns false

You are mistaken about what is not safe.

It is not safe to assume that an arbitrary decimal representation, even if it is short (written in decimal), is represented exactly in binary floating-point. It is not safe to assume that - between binary floating-point values produces anything other than the nearest representable floating-point value to the mathematical result (and in particular, it is not safe to assume that it always produces the mathematical result).

d1 == d2 is perfectly safe, and so is (decimal)d1 == (decimal)d2. The first one will not always return what you appear to want, but neither will the second, because it has no reason to according to the principles laid out above. Once d1 and d2 have been computed, the approximations have already been done:

  • representation approximation, because you appear to think that 0.11 should be the mathematical value 11/100, and it's not, it's the nearest representable value;
  • operation approximation, because the mathematical result of 0.44 minus 0.33 may not be representable exactly, in which case the nearest representable value will be used.

The fact that these approximations compound and, when adding the third approximation of converting to decimalin C#, result in the value you expected is pure coincidence. If anything should be commonly known, it should be that it is too late to fix an approximation after it has occurred and that adding another approximation will not really help. See the first example in this article, Excel, another programmable product from Microsoft.

Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
1

In this case you may get the desired result but generally your answer is NO. When you store a value in a double then casting it to a decimal do not enhance its precision (link).

Consider the below example. The equality returns true but we expect false.

double myDouble1 = 0.11;
double myDouble2 = 0.10999999999999999;
Console.WriteLine((decimal)myDouble1 == (decimal)myDouble2); //true, but we expect false

But if you store those values as decimal, then the you will get the proper result.

decimal myDecimal1 = 0.11M;
decimal myDecimal2 = 0.10999999999999999M;
Console.WriteLine(myDecimal1 == myDecimal2); //false
Community
  • 1
  • 1
Hossein Narimani Rad
  • 31,361
  • 18
  • 86
  • 116
0

It's always safe, provided you know the conversion semantics between floating point and decimal types, and recognize the impact on the implementing code.

Which is to say, unless you have a good reason for the cast, you are safer following the standard approach of checking equality for some given tolerance. (E.g. (0.999 - 1.00) < 0.01.)

From Explicit Numeric Conversions Table (C# Reference):

  • When you convert float or double to decimal, the source value is converted to decimal representation and rounded to the nearest number after the 28th decimal place if required. Depending on the value of the source value, one of the following results may occur:

    • If the source value is too small to be represented as a decimal, the result becomes zero.

    • If the source value is NaN (not a number), infinity, or too large to be represented as a decimal, an OverflowException is thrown.

    • When you convert decimal to float or double, the decimal value is rounded to the nearest double or float value.

Austin Drenski
  • 506
  • 4
  • 10
0

The MSDN says:

The decimal type has 29 digits of precision, whereas a difference between these two values can be detected only with 30 digits of precision.

There is example that demonstrates comparison trouble for decimal values. For details see https://msdn.microsoft.com/library/system.decimal.compare(v=vs.110).aspx

Alexander
  • 4,420
  • 7
  • 27
  • 42
-1

double stores approximate values and thus this case will not be equal:

double d1 = 0.11;
double d2 = 0.44 - 0.33;
Console.WriteLine(d1 == d2);

decimal stores values more closer to what they are than does a double so this will be equal:

Console.WriteLine((decimal)d1 == (decimal)d2);

But it is important to note that even decimal is an approximate value. For example, you could not store the Pi's exact value using a decimal.

It is commonly known that it is not safe to compare double values in .net.

It all depends on the level of precision your calculation needs. For some calculations it is fine to use it, for others it is not.

System.Double (double in C#) and System.Single (float in C#) are stored as approximate values. For example:

double x = 0.1d;

x will store the closest available double to that value.


Important Distinction

It is very important to note that unlike double, a decimal will not store itself by normalizing but it will remember the zeros. For example, try the following code:

decimal dec1 = 1.000000000m;
double dbl11 = 1.000000000;

Console.WriteLine(dec1);  // outputs: 1.000000000 (remembers all 9 zeros)
Console.WriteLine(dbl11); // outputs: 1

<==Try Me==>

A decimal is stored as base 10 in memory but double and float are stored in base 2. All of them have the following components: a mantissa, an exponent and a sign. For example:

44.5 could be represented in "decimal floating point" as mantissa 4.45 with an exponent of 1, whereas 4450 would have the same mantissa but an exponent of 3.

You can read more on Floating Points and Decimals if are curious.


Kind of off-topic but Interesting Question

I was talking to someone about decimals and approximations etc. and they brought up this question: Imagine you own a store and you buy 3 items for $1.00. You want to cut even so which customer will have to take the hit and pay the extra penny?

CodingYoshi
  • 25,467
  • 4
  • 62
  • 64
  • 2
    "`double` stores approximate values" - no, a `double` *stores* a very precise value. Many arithmetic operations (including conversion from decimals in source code) will lose information in order to produce the `double`, but the value itself is precise. – Jon Skeet May 12 '17 at 05:44
  • Then how come the first case does not equal? Sorry will reply tomorrow in case you respond-it is late and need to sleep :) – CodingYoshi May 12 '17 at 05:53
  • 2
    Because the values stored are different - they're *exactly* 0.11000000000000000055511151231257827021181583404541015625 and 0.10999999999999998667732370449812151491641998291015625. The approximation happens when performing the conversion from the decimal representation in the source code to `double`, and potentially again when performing the subtraction - but the `double` values themselves are not approximate. It's like talking about `int` - if you write `int x = (int) 10.6;` then it will store the value 10. Information is lost in the conversion, but the value is exactly 10. – Jon Skeet May 12 '17 at 06:06
  • @JonSkeet If 0.11 is stored as 0.11000000000000000055511151231257827021181583404541015625, then how is that not an approximation? Sure approximation happens during conversion but in the first case there is no conversion. – CodingYoshi May 12 '17 at 18:16
  • 2
    Of course there's a conversion - it's from decimal floating point (in the source code) to binary floating point. It's not that 0.11 is stored as 0.110000000000000000555... - it's that 0.110000000000000000555 is stored precisely, and the compiler determines that the nearest value to 0.11 is 0.110000000000000000555. Understanding where the approximation happens - where information is lost - is important, IMO. – Jon Skeet May 12 '17 at 18:30
  • Ok i kinda see your point but can you please elaborate on this point and where that is happening: `Of course there's a conversion - it's from decimal floating point (in the source code) to binary floating point.` – CodingYoshi May 12 '17 at 21:59
  • I'm not sure what there is to elaborate on: a) it's happening at compile-time; b) you're specifying the value in decimal, and the compiler finds the nearest `double` to that value. – Jon Skeet May 13 '17 at 06:49