比较C#中的double值
我正在使用C#。
我有一个双variables称为x。 在代码中,x被赋值为0.1,我在比较x和0.1的'if'语句中检查它
if(x==0.1) { ---- }
不幸的是它没有inputif语句
1)我应该使用双或双?
2)这背后的原因是什么? 你能为此提出一个解决scheme吗?
由于计算机如何存储浮点值,这是一个标准问题。 在这里search“浮点问题”,你会发现大量的信息。
总之 – 一个浮点/双精度不能精确地存储0.1。 它总是有点closures。
您可以尝试使用以十进制表示法存储数字的decimal
types。 因此,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 …
由于二进制表示法不能精确地存储,因此以四舍五入的方式进行存储。 因此你的问题。
double
和Double
是相同的( double
是Double
的别名),可以互换使用。
将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
之外的方法,我相信它是非常安全的扩展。
那么你可以比较x
和y
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)我应该使用双或双?
Double
和double
是同样的事情。 double
只是一个C#关键字,用作类的别名System.Double
最常见的是使用别名! 对于string
( System.String
), int
( System.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; }