众所周知,将双值与==运算符进行比较是不安全的。在此示例中,它返回false:
double d1 = 0.11;
double d2 = 0.44 - 0.33;
Console.WriteLine(d1 == d2);
但是,如果我们将值转换为十进制,则返回true:
double d1 = 0.11;
double d2 = 0.44 - 0.33;
Console.WriteLine((decimal)d1 == (decimal)d2);
比较从double转换的小数是否总是安全的,或者在某些情况下会产生意外的结果?
更新: Hossein的例子很好,它表明它可能是错误的。但我更感兴趣的是看看是否存在相反的例子,当我们期望十进制值相等而它们不相同时。甚至更宽,当我们从double转换为十进制时会发生什么。
答案 0 :(得分:2)
众所周知,比较.net中的double值是不安全的。在此示例中,它返回false
你错了什么不安全。
假设任意十进制表示(即使它很短(以十进制表示))完全用二进制浮点表示是不安全的。假设二进制浮点值之间的-
产生除数学结果的最近可表示浮点值之外的任何东西是不安全的(特别是,假设它总是产生数学结果是不安全的)结果)。
d1 == d2
非常安全,(decimal)d1 == (decimal)d2
也是如此。第一个不会总是返回你想要的东西,但第二个也不会,因为它没有理由按照上面列出的原则。计算出d1
和d2
后,已经完成了近似值:
0.11
应该是数学值11/100,而它不是,它是最接近的可表示值; 0.44
减去0.33
的数学结果可能无法准确表示,在这种情况下,将使用最接近的可表示值。这些近似值复合的事实,当在C#中添加转换为decimal
的第三个近似值时,会导致您期望的值纯属巧合。如果任何事情应该是众所周知的,那么在它发生之后修复近似值就太迟了,并且添加另一个近似值并不会真正有用。请参阅本文中的第一个示例Excel,这是Microsoft的另一个可编程产品。
答案 1 :(得分:1)
在这种情况下,您可能会得到所需的结果,但通常您的答案是否。 将值存储在 double 中然后将其转换为 decimal 时,不会增强其精度(link)。
考虑下面的例子。等式返回true,但我们期望错误。
double myDouble1 = 0.11;
double myDouble2 = 0.10999999999999999;
Console.WriteLine((decimal)myDouble1 == (decimal)myDouble2); //true, but we expect false
但如果您将这些值存储为 decimal ,那么您将获得正确的结果。
decimal myDecimal1 = 0.11M;
decimal myDecimal2 = 0.10999999999999999M;
Console.WriteLine(myDecimal1 == myDecimal2); //false
答案 2 :(得分:0)
它始终是安全的,提供您知道浮点和小数类型之间的转换语义,和会识别对实现代码的影响。
也就是说,除非你有充分的理由进行演员表,否则你会更安全地遵循检查某些给定公差的相等性的标准方法。 (例如(0.999 - 1.00) < 0.01
。)
来自Explicit Numeric Conversions Table (C# Reference):
将float或double转换为十进制时,源值为 转换为十进制表示并四舍五入到最接近的数字 如果需要,在第28个小数位后。取决于的价值 源值,可能会出现以下结果之一:
如果源值太小而无法表示为小数,则为 结果变为零。
如果源值为NaN(非数字),无穷大或太大 表示为十进制,抛出OverflowException。
将decimal转换为float或double时,十进制值为 四舍五入到最接近的double或float值。
答案 3 :(得分:-1)
double
存储近似值,因此这种情况不相等:
double d1 = 0.11;
double d2 = 0.44 - 0.33;
Console.WriteLine(d1 == d2);
decimal
存储的值比double
更接近它们,所以这将是相等的:
Console.WriteLine((decimal)d1 == (decimal)d2);
但重要的是要注意,即使decimal
是近似值。例如,您无法使用小数存储 Pi 的确切值。
众所周知,比较.net中的双重值是不安全的。
这一切都取决于您的计算所需的精确度。对于某些计算,使用它是好的,而对于其他计算则不然。
System.Double
(C#中的double
)和System.Single
(C#中的float
)存储为近似值。例如:
double x = 0.1d;
x会将最接近的可用双精度值存储到该值。
重要区别
非常重要的是要注意,与double
不同,decimal
不会通过规范化存储自身,但会记住零。例如,尝试以下代码:
decimal dec1 = 1.000000000m;
double dbl11 = 1.000000000;
Console.WriteLine(dec1); // outputs: 1.000000000 (remembers all 9 zeros)
Console.WriteLine(dbl11); // outputs: 1
decimal
作为基数10存储在内存中,但double
和float
存储在基数2中。所有这些都包含以下内容:尾数,指数和符号。例如:
44.5可以表示在&#34;十进制浮点&#34;如尾数4.45,指数为1,而4450则具有相同的尾数但指数为3.
如果您感到好奇,可以在Floating Points和Decimals上阅读更多内容。
一种偏离主题但有趣的问题
我正在和某人谈论小数和近似等问题,他们提出了这个问题:想象一下,你拥有一家商店,你以1.00美元购买3件商品。你想削减哪个客户将不得不采取打击并支付额外的一分钱?