.NET - 浮点比较

Alp*_*ğlu 6 .net floating-point comparison

直接比较.net中的浮点数(double,float)是否相等是不安全的.变量中的double值可能会随着时间的推移而变化很小.例如,如果将变量num(double)设置为对象的0.2,则在该对象在内存中等待一段时间后,您可能会发现num变为0.1999999999999.因此,在这种情况下,num == 0.2将为false.我对此问题的解决方案是创建一个属性来舍入数字:

double Num
{
get{ return Math.Round(num, 1); }
}
Run Code Online (Sandbox Code Playgroud)

调用Num的get并返回结果后,在比较时(Num == 0.2),这个返回的数字是否会再次变为0.19?它不太可能但是有保证吗?

Ode*_*ded 6

不,不保证.

来自MSDN - Math.Round:

此方法的行为遵循IEEE标准754第4节.这种舍入有时称为舍入到最近,或者是银行家的舍入.它最大限度地减少了在单个方向上始终舍入中点值所导致的舍入误差.

(强调我的)

重点是 - 它最小化,而不是确保.


在比较浮点类型时,您应该始终针对epsilon进行测试 - 这是您不关心的最小值.

示例改编自此处:

double dValue = 0.2;

var diff = Math.Abs(num - dValue);
if( diff < 0.0000001 ) // need some min threshold to compare floating points
{
  // treat as equal
}
Run Code Online (Sandbox Code Playgroud)

推荐阅读:每个计算机科学家应该知道的关于浮点运算的内容.


ian*_*lly 3

无论您是否相信,这都是有意的行为,并且符合某些 IEEE 标准。

不可能以单个二进制表示形式完全保真地表示模拟的日常值,例如大量数字或小分数。.NET 中的浮点数(例如 float 或 double)在为它们分配数字时会尽力最小化错误,因此当您为变量分配 0.2 时,语言会尽力选择错误​​最小的表示形式。

这并不是说这个数字在记忆中会以某种方式退化——这是一个故意的步骤。如果您要比较浮点数,则应始终在比较的两侧留出可接受的区域。您所表示的 0.2 接近非常多的小数位。这对于您的应用来说足够好吗?看起来很刺眼,但实际上这是一个很小的错误。在比较双精度数和浮点数(与整数或彼此之间)时,您应该始终考虑可接受的精度,并接受预期结果两侧的范围。

您还可以选择使用其他类型,例如小数,它在小数位上具有非常好的精度 - 但与浮点数和双精度数相比也非常大。