比较来自双打

时间:2017-05-12 04:46:41

标签: c# .net floating-point

众所周知,将双值与==运算符进行比较是不安全的。在此示例中,它返回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转换为十进制时会发生什么。

4 个答案:

答案 0 :(得分:2)

  

众所周知,比较.net中的double值是不安全的。在此示例中,它返回false

你错了什么不安全。

假设任意十进制表示(即使它很短(以十进制表示))完全用二进制浮点表示是不安全的。假设二进制浮点值之间的-产生除数学结果的最近可表示浮点值之外的任何东西是不安全的(特别是,假设它总是产生数学结果是不安全的)结果)。

d1 == d2非常安全,(decimal)d1 == (decimal)d2也是如此。第一个不会总是返回你想要的东西,但第二个也不会,因为它没有理由按照上面列出的原则。计算出d1d2后,已经完成了近似值:

  • 表示近似,因为您似乎认为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

<==Try Me==>

decimal作为基数10存储在内存中,但doublefloat存储在基数2中。所有这些都包含以下内容:尾数,指数和符号。例如:

  

44.5可以表示在&#34;十进制浮点&#34;如尾数4.45,指数为1,而4450则具有相同的尾数但指数为3.

如果您感到好奇,可以在Floating PointsDecimals上阅读更多内容。

一种偏离主题但有趣的问题

我正在和某人谈论小数和近似等问题,他们提出了这个问题:想象一下,你拥有一家商店,你以1.00美元购买3件商品。你想削减哪个客户将不得不采取打击并支付额外的一分钱?