经验法则测试C#中两个双打的相等性?

dev*_*xer 14 c# floating-point

假设我有一些代码可以执行一些浮点运算并将值存储在双精度数中.由于某些值无法以二进制形式完美表示,如何在合理程度的确定性下测试相等性?

我如何确定"合理"是什么意思?

可以double.Epsilon用某种方式吗?


更新

几件事.正如@ ho1所指出的那样,文档double.Epsilon指出,当比较两个双精度表示相等时,你可能想要一个远大于epsilon的值.以下是文档中的相关段落:

由于其最低有效位数的差异,两个明显等效的浮点数可能无法相等.例如,C#表达式(double)1/3 ==(double)0.33333,不比较相等,因为左侧的除法运算具有最大精度,而右侧的常量仅精确到指定的数字.如果创建一个自定义算法来确定是否可以将两个浮点数视为相等,则必须使用大于Epsilon常量的值来确定两个值相等的可接受的绝对差值.(通常,差异的差异比Epsilon大很多倍.) - http://msdn.microsoft.com/en-us/library/system.double.epsilon.aspx

......但问题是,多少倍?

如果它会影响你的答案,我的特殊情况涉及几何计算(例如使用点和向量的点积和交叉积).在某些情况下,您会根据是否A == B,A > B或者得出不同的结论A < B,因此我正在寻找一个关于如何确定等价窗口大小的良好经验法则.

Jos*_*ley 18

使用double.Epsilon不一定有效. double.Epsilon给出大于零的最小可表示值.但是,由于浮点数的实现方式,它们的精度越低,它们越远离零,因此对于double.Epsilon彼此非常接近的两个大数,检查差异可能会失败.

详细信息:base-2浮点数表示为有效数字 - 1到2之间的数字 - 乘以2加到某个指数.double对于有效数的小数部分有52位,对指数有11位精度.如果指数是一个非常大的负值且有效数为0,那么你得到的值接近double.Epsilon,但是如果你的指数足够大,那么即使两个有效数值的非常小的差值也会产生远大于的值double.Epsilon.

有关如何测试两个浮点数是否相等的完整讨论,请参阅Bruce Dawson的"比较浮点数,2012版".总而言之,有三种主要的比较方法:

使用绝对差异

正如Joel Coehoorn的例子,但要非常小心地选择一个适当大小的值,这与Joel的例子不同.

使用相对差异

类似于以下内容:

if (Math.Abs(a - b) / b <= maxRelativeError)
{
    return true;
}
Run Code Online (Sandbox Code Playgroud)

但是,有并发症; 你应该除以两个值中较大的一个,并且这个函数对于接近于零的值表现不佳,除非你还添加一个最大绝对差值的检查.有关详细信息,请参阅论文

使用最后一个单位

使用最后位置单位(ULP)的比较意味着检查有效位数的最后部分.(本文将其称为"使用整数进行比较.")这是一种更复杂的方法,但非常强大.本文提供了C语言的源代码; 对于C#,你可以使用BitConverter.DoubleToInt64Bits.

响应您的编辑

"多少倍?" 这实际上是您的应用程序域的问题,这可能是.NET Framework不提供默认方法的原因,但我很幸运使用ULP比较,最大ULP差异为4.