Is it safe to compare decimals casted from doubles
众所周知,用==运算符比较双精度值是不安全的。在本例中,它返回false:
1 2 3 | double d1 = 0.11; double d2 = 0.44 - 0.33; Console.WriteLine(d1 == d2); |
但是,如果我们将值强制转换为十进制,它将返回true:
1 2 3 | double d1 = 0.11; double d2 = 0.44 - 0.33; Console.WriteLine((decimal)d1 == (decimal)d2); |
比较双精度小数是否总是安全的,或者在某些情况下可以给出意外的结果?
更新:侯赛因的例子很好,这表明它可能是错误的。但我更感兴趣的是看看是否有相反的例子,当我们期望十进制值相等而它们不相等时。更广泛地说,当我们从双精度转换为十进制时会发生什么。
It is commonly known that it is not safe to compare double values in .net. In this example it returns false
你误解了什么是不安全的。
假设一个任意的十进制表示,即使它很短(用十进制表示),也不能完全用二进制浮点表示。假设二元浮点值之间的
- 表示近似,因为您似乎认为
0.11 应该是11/100的数学值,而不是,它是最近的可表示值; - 运算近似,因为
0.44 减去0.33 的数学结果可能无法精确表示,在这种情况下,将使用最接近的可表示值。
这些近似值是复合的,加上转换到c中的
在这种情况下,您可能会得到期望的结果,但通常您的答案是否定的。当您将一个值存储在double中,然后将其转换为十进制时,不会提高其精度(link)。
考虑下面的例子。相等返回真,但我们期望是假。
1 2 3 | double myDouble1 = 0.11; double myDouble2 = 0.10999999999999999; Console.WriteLine((decimal)myDouble1 == (decimal)myDouble2); //true, but we expect false |
但是如果您将这些值存储为十进制,那么您将得到正确的结果。
1 2 3 | decimal myDecimal1 = 0.11M; decimal myDecimal2 = 0.10999999999999999M; Console.WriteLine(myDecimal1 == myDecimal2); //false |
MSDN表示:
The decimal type has 29 digits of precision, whereas a difference between these two values can be detected only with 30 digits of precision.
有一个例子演示了十进制值的比较问题。有关详细信息,请参阅https://msdn.microsoft.com/library/system.decimal.compare(v=vs.110).aspx
如果您知道浮点和十进制类型之间的转换语义,并认识到对实现代码的影响,那么它总是安全的。
也就是说,除非你有一个好的铸造理由,否则你会更安全地遵循标准方法来检查某些给定公差的相等性。(如
从显式数字转换表(C引用):
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.
1 2 3 | double d1 = 0.11; double d2 = 0.44 - 0.33; Console.WriteLine(d1 == d2); |
1 | Console.WriteLine((decimal)d1 == (decimal)d2); |
但值得注意的是,即使是
It is commonly known that it is not safe to compare double values in .net.
这完全取决于您计算所需的精度水平。对于某些计算,可以使用它,而对于其他计算则不行。
1 | double x = 0.1d; |
x将存储最接近该值的两倍。
重要区别
需要注意的是,与
1 2 3 4 5 | decimal dec1 = 1.000000000m; double dbl11 = 1.000000000; Console.WriteLine(dec1); // outputs: 1.000000000 (remembers all 9 zeros) Console.WriteLine(dbl11); // outputs: 1 |
<==尝试==>
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.
如果你好奇的话,你可以多读一些浮点数和小数。
有点离题但很有趣的问题
我正在和某人谈论小数和近似值等问题,他们提出了这样一个问题:假设你拥有一家商店,你花1.00美元买了3件商品。你想减价,这样哪个顾客就必须接受打击并支付额外的一分钱?