我注意到在添加两个双打时出现了一些奇怪的行为,有时候它是正确的,有时候也没有!
这是第一个例子:
double num1 = 0.1 + 0.7; //Guess the result?
轻松 - 0.8 !!!不是吗?
看看奇怪的结果:
猜猜是什么,if语句在else块内,并打印num1
- 不,它不打印0.799999999999993,它打印0.8。
所以我向前迈出了一步,尝试了这段代码:
if (0.1 + 0.7 == 0.8) //Returns false ??
{
Console.WriteLine("Correct");
}
好的,很奇怪,但现在我找到了正确的方法,它应该使用f(float)。我记得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");
}
调试时会生成手表:
有人可以解释我这里有什么问题吗?那些错误吗?
提前致谢。
答案 0 :(得分:6)
浮点运算不精确。您应该阅读文章What Every Computer Scientist Should Know About Floating-Point Arithmetic。如果您希望计算在十进制意义上是精确的,则应使用decimal
类型,其中0.1m + 0.7m == 0.8m
为true
。您应该注意decimal
使用浮点数,就像float
和double
一样,除了基数是10而不是2.因为处理基数2是如此容易和高度优化,decimal
要慢得多。它也有自己的不准确性,只有在处理可用短数字(如0.7
和0.1
)表示的数字时才准确,这对财务数据有利。
另一篇了解浮点数的有用文章是Wikipedia's Double-precision floating-point format。
答案 1 :(得分:1)
在内部,double存储在base-2中的位中。 所有整数都可以用base-2完全表示,但是当它涉及分数时,它就是另一个问题。
与我们在十进制系统中无法精确地表示1/3结果的方式相同,在二元系统中,您无法表示所有分数。由于十进制是基数为10,因此很容易以十进制表示x / 10,但在基数2中,某些x是不可能的。
因此,无论何时使用双打,都要注意一些舍入错误。
出于这个原因,只要需要确切的数字,就不会使用双打,就像货币金额一样。
答案 2 :(得分: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
答案 3 :(得分:1)
以下是答案:http://www.codeproject.com/Articles/383871/Demystify-Csharp-floating-point-equality-and-relat
浮动值仅为近似值。要比较两个浮点值,您需要检查它们之间的差异。差异不能大于float.Epsilon
使用它来比较这些值:
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);
}