2013-10-26 45 views
1

我注意到一些奇怪的行为,同时添加两个双打,有时它工作正确,有时不是!C#为一个简单的数学运算获得奇怪的结果

这是第一个例子:

double num1 = 0.1 + 0.7; //Guess the result? 

容易 - 0.8!或不?

看看奇怪的结果:

enter image description here

你猜怎么着,if语句的else块里面去,并打印num1 - 不,它不打印0.799999999999993,它打印0.8。

所以我一直向前走了一步,并试图将此代码:

if (0.1 + 0.7 == 0.8) //Returns false ?? 
{ 
    Console.WriteLine("Correct"); 
} 

OK,奇怪的,但现在我找到了正确的方法,就应该使用F(浮动)。正如我记得double有很多空格,所以它可以包含更多的数字,也许这是原因。

float num2 = 0.1f + 0.7f; 

if (num2 == 0.8f) //Perfect - finally works !!! 
{ 
    Console.WriteLine("Correct"); 
} 
else 
{ 
    Console.WriteLine(num2); 
} 

但是现在,我试试这个 - 并且它又返回false,为什么?

if (0.1f + 0.7f == 0.8f) //Returns false :(
{ 
    Console.WriteLine("Correct"); 
} 

手表结果调试它的时候:

enter image description here

谁能给我解释一下什么是错在这里?那些错误?

在此先感谢。

+0

的可能重复(http://stackoverflow.com/questions/753948/why-is-floating-point-arithmetic-in-c-sharp-imprecise) – MichaC

+0

@MichaC提问的方式完全不同,也不一样。 –

+0

因为你在这个问题上付出了很多努力,但最终它是一回事。这仅仅是因为C#中的浮点运算不精确。在这个网站上已经回答了1000次^^ – MichaC

回答

4

浮点运算不精确。您应该阅读文章What Every Computer Scientist Should Know About Floating-Point Arithmetic。如果您希望计算精确到十进制,则应使用decimal类型,其中0.1m + 0.7m == 0.8mtrue。您应该注意,decimal使用的浮点数与floatdouble一样,除了基数为10,而不是2.由于处理基数2更容易且高度优化,所以decimal要慢得多。它也有它自己的不准确之处,只有在处理可以以小数位数表示的数字(如0.70.1)时才是精确的,这对财务数据非常有用。

理解浮点数的另一篇有用的文章是Wikipedia's Double-precision floating-point format

+0

+1的链接:) – oerkelens

1

在内部,double以base-2的位数存储。 所有的整数可以完全用base-2表示,但是当涉及到分数时,它是另一个问题。

与我们不能精确地在我们的十进制系统中精确地再现1/3的结果相同,在二进制系统中,您不能表示所有分数。由于十进制是10的基数,很容易用十进制表示x/10,但在基数2中,某些x不可能。

因此,无论何时使用双打,都要注意一些舍入误差。

由于这个原因,只要需要确切的数字就不会使用双打,就像货币金额一样。

+0

在第二段中,当然你的意思是“对某些'x'不可能”。例如,5/10 = 1/2可以精确地表示为基数2的浮点数。 – delnan

+0

事实上,你是对的,我编辑它:) – oerkelens

+0

但声明是正确的:在二进制0.1有相同的问题,1/3有小数。 – duffymo

1

调用Math.Round方法以确保两个值具有相同的精度。下面的示例修改前面的例子中,使得两个分数值等同于使用这种方法:

using System; 

public class Example 
{ 
    public static void Main() 
    { 
     double value1 = .333333333333333; 
     double value2 = 1.0/3; 
     int precision = 7; 
     value1 = Math.Round(value1, precision); 
     value2 = Math.Round(value2, precision); 
     Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2)); 
    } 
} 
// The example displays the following output: 
//  0.3333333 = 0.3333333: True 
1

下面是答案:http://www.codeproject.com/Articles/383871/Demystify-Csharp-floating-point-equality-and-relat

浮动值仅仅是近似值。要比较两个浮点值,您需要检查它们之间的差异。该差异不能大于float.Epsilon

使用此对这些值进行比较:?在C#不精确为什么浮点运算]

bool AlmostEqualDoubles(double nVal1, double nVal2, int nPrecision = 6) 
{ 
    nPrecision = Math.Max(Math.Min(16, nPrecision), 0); 
    double nEpsilon = 1.0; 
    for (int i = 0; i < nPrecision; i++) nEpsilon *= 0.1;  
    return (nVal2 - nEpsilon) < nVal1 && nVal1 < (nVal2 + nEpsilon); 
} 
+0

“浮动值只是近似值”并不严格。说明这一点的更好方法是许多十进制值不会精确映射到IEEE浮点数。但是,当映射到浮点数时(例如4,2,1,5 .25,.125等),可以使用2的幂分解的值将具有精确的值。在数字如1/3等的小数点世界中会出现同样的问题。 – spender