比较C#中的double值

我正在使用C#。

我有一个双variables称为x。 在代码中,x被赋值为0.1,我在比较x和0.1的'if'语句中检查它

if(x==0.1) { ---- } 

不幸的是它没有inputif语句

1)我应该使用双或双?

2)这背后的原因是什么? 你能为此提出一个解决scheme吗?

由于计算机如何存储浮点值,这是一个标准问题。 在这里search“浮点问题”,你会发现大量的信息。

总之 – 一个浮点/双精度不能精确地存储0.1。 它总是有点closures。

您可以尝试使用以十进制表示法存储数字的decimaltypes。 因此,0.1将精确地表示。


你想知道原因:

浮点/双精度被存储为二进制分数,而不是小数。 为了显示:

12.34十进制表示(我们使用的)是指1 * 10 1 + 2 * 10 0 + 3 * 10 -1 + 4 * 10 -2 。 计算机以相同的方式存储浮点数,除了使用基2:10.01表示1 * 2 1 + 0 * 2 0 + 0 * 2 -1 + 1 * 2 -2

现在,你可能知道有一些数字不能用我们的十进制符号表示。 例如,十进制表示法中的1/3是0.3333333 …除了无法精确表示的数字不同之外,同样的事情发生在二进制表示法中。 其中数字是1/10。 在二进制表示法是0.000110011001100 …

由于二进制表示法不能精确地存储,因此以四舍五入的方式进行存储。 因此你的问题。

doubleDouble是相同的( doubleDouble的别名),可以互换使用。

将double与另一个值进行比较的问题是double是近似值,而不是精确值。 所以当你将x设置为0.1时,实际上可以存储为0.100000001或类似的东西。

您应该检查差异是否小于定义的最小差异(容差),而不是检查相等性。 就像是:

 if (Math.Abs(x - 0.1) < 0.0000001) { ... } 

由于四舍五入,比较浮点数并不总是精确。 比较

 (x == .1) 

电脑真的比较

 (x - .1) vs 0 

由于浮点数如何在机器上performance出来,所以并不总能精确地表示出结果。 因此,你得到一个非零值,条件评估为false

为了克服这个比较

 Math.Abs(x- .1) vs some very small threshold ( like 1E-9) 

您需要将XY上的Math.Abs和一个value进行比较。

您可以使用以下扩展方法方法

 public static class DoubleExtensions { const double _3 = 0.001; const double _4 = 0.0001; const double _5 = 0.00001; const double _6 = 0.000001; const double _7 = 0.0000001; public static bool Equals3DigitPrecision(this double left, double right) { return Math.Abs(left - right) < _3; } public static bool Equals4DigitPrecision(this double left, double right) { return Math.Abs(left - right) < _4; } ... 

既然你很less调用除了ToString之外的方法,我相信它是非常安全的扩展。

那么你可以比较xy

if(x.Equals4DigitPrecision(y))

从文档 :

比较中的精确性应该谨慎使用等式方法,因为两个值看起来相等的值由于两个值的精确度不同而可能不相等。 以下示例报告Double值.3333和1除以3返回的Double不相等。

一个推荐的技术不是比较平等,而是涉及定义两个值之间可接受的差值(例如其中一个值的0.01%)。 如果两个值之间的差值的绝对值小于或等于该裕度,则差异可能是由于精度的差异,因此这些值可能是相等的。 以下示例使用此技术来比较.33333和1/3,前面的代码示例发现不相等的两个Double值。

所以如果你真的需要双倍的话,你应该使用文档中描述的技术。 如果可以,请将其更改为小数点。 它会变慢 ,但是你不会有这种types的问题。

双和双是相同的。

为此,请参阅http://www.yoda.arachsys.com/csharp/floatingpoint.html 。 简而言之:双倍不是一个确切的types,“x”和“0.1”之间的微小差异将会抛出。

由于四舍五入和内部表示问题,精确比较浮点值并不总是奏效。

尝试不精确的比较:

 if (x >= 0.099 && x <= 0.101) { } 

另一种select是使用十进制数据types。

使用decimal 。 它没有这个“问题”。

1)我应该使用双或双?

Doubledouble是同样的事情。 double只是一个C#关键字,用作类的别名System.Double最常见的是使用别名! 对于stringSystem.String ), intSystem.Int32

另请参阅内置types表(C#参考)

双(在某些语言中称为float)由于四舍五入问题而出现问题,只有在需要近似值时才是好的。

十进制数据types做你想要的。

在.NET C#中,十进制和十进制的引用是一样的,就像double和doubletypes一样,它们都指向相同的types(小数点和双精度,但是,正如你所看到的)。

请注意,十进制数据types有一些相关的成本,所以如果你正在查看循环等,请谨慎使用它。

浮点数表示是众所周知的不准确的(因为浮点数被内部存储),例如,x可能实际上是0.0999999999或0.100000001,并且你的条件将失败。 如果你想确定浮动是否相等,你需要具体确定它们是否等于一定的容差范围内。

 if(x - 0.1 < tol) 

作为基本规则:

在大多数情况下,双重表示是足够好的,但在某些情况下可能会失败。 如果您需要完整的精度(如财务应用程序),请使用十进制值。

大多数双打问题并非来自直接比较,而是由于几个math运算的积累导致的,这些math运算由于四舍五入和小数部分的错误(特别是乘法和除法)而呈指数地扰乱值。

检查你的逻辑,如果代码是:

 x = 0.1 if (x == 0.1) 

它不应该失败,这很容易失败,如果通过更复杂的手段或操作来计算X值,那么debugging器使用的ToString方法使用的是一个聪明的四舍五入,也许你可以做同样的事情(如果这太冒险了回到使用小数):

 if (x.ToString() == "0.1") 

我发现一个很酷的黑客是使用.GetHashCode()方法,它返回一个int,表示double,即

(0.4d + 0.3d + 0.2d + 0.1d).GetHashCode() //returns -1072693248

1d.GetHashCode() //returns 1072693248

所以你现在注意到我们可以使用这样的东西

 public static bool AccurateEquality(double first,double second) { return Math.Abs(first.GetHashCode()) == Math.Abs(second.GetHashCode()); } 

用法: AccurateEquality((0.4d + 0.3d + 0.2d + 0.1d),1) //returns true

while: (0.4d + 0.3d + 0.2d + 0.1d) == 1d //returns false

我尝试了多个案件,似乎运作良好。

从Java代码库中获取一个提示,尝试使用.CompareTo并testing零比较。 这假设.CompareTo函数以准确的方式考虑了浮点相等性。 例如,

 System.Math.PI.CompareTo(System.Math.PI) == 0 

这个谓词应该返回true

大多数以上的方式或愚蠢的扩展方法!

 public static bool EqualsTo(this double value, double value2) { var bytes1 = BitConverter.GetBytes(value); var bytes2 = BitConverter.GetBytes(value2); var long1 = BitConverter.ToInt64(bytes1, 0); var long2 = BitConverter.ToInt64(bytes2, 0); return long1 == long2; }