.NET - 浮点数比较

6
在.NET中,直接比较浮点数(double、float)的相等性是不安全的。变量中的双精度值可能会随着时间的推移而发生微小的变化。例如,如果您将变量num(double类型)设置为某个对象的0.2,然后该对象在内存中等待一段时间,您可能会发现num变成了0.1999999999999。所以在这种情况下,num == 0.2将会是假的。解决此问题的方法是创建一个属性来四舍五入保留小数。
double Num
{
get{ return Math.Round(num, 1); }
}

在调用get Num并返回结果后,这个返回的数字在比较(Num==0.2)的时候会改变回0.19吗?虽然可能性不大,但能否保证它不会发生呢?

就此而言:变量中的值从0.2到0.19999999999 不会改变(即它不会随时间泄漏、流失或丢失几个位)。事实是,0.2在二进制浮点变量中根本无法精确表示,因此它被存储为双精度或单精度可以表示的最接近的可能值。这就是为什么你会得到类似于0.199999999999或0.20000000001的结果。 - Rudy Velthuis
4个回答

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
}

推荐阅读:计算机科学家应该了解的浮点运算知识


3

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

在单一的二进制表示中,不可能完全保留模拟日常值,例如大数或小分数。 .NET中的浮点数(如float或double)尽力在给它们赋值时最大限度地减少误差,因此当你将0.2赋值给变量时,语言会尽力选择误差最小的表示方法。

数字并不是在内存中以某种方式退化 - 这是一个故意的步骤。如果你正在比较浮点数,你应该总是允许一个可接受的区域来进行比较。你对0.2的表示接近于很多位小数的一个非常大的数字。这对你的应用程序是否足够好呢?它看起来让人惊讶,但实际上是一个非常小的误差。当比较双精度浮点数和单精度浮点数(与整数或彼此之间),你应该始终考虑可接受的精度,并接受期望结果两侧范围内的数值。

你还可以选择使用其他类型,如decimal,它在小数位上具有极高的精度 - 但与浮点数和双精度浮点数相比也要大得多。


1
十进制不仅更大(128位),而且最重要的是非常慢,因为它没有硬件支持,所以每个操作都必须在软件中完成。 - Rudy Velthuis

2

变量本身不会自行更改。如果在某个时间点a == b,则在修改a或b之前,a == b将一直成立。

您可能遇到与浮点数据类型表示相关的问题,但目前尚不清楚具体是什么问题。显然,您目前的“解决方案”几乎肯定不是一个好主意。


实际上我看到它改变了。我将一个变量设置为0.2,过了一段时间后它变成了0.1999999(在调试模式下)。但你可能会说它没有改变,这是浮点表示的问题。当我过一段时间再次查看同一变量时,它可能又变成了0.2。 - Alp
1
不,你从未见过它改变。变量不会自己改变。如果有一天发生这种情况,我的世界就会崩溃,我会寻找新的职业。 - David Heffernan
变量不会改变,当然也不会来回变化。可能发生的情况是,调试器在不同的位置显示数字时可能具有不同的精度。例如,内存窗口、本地窗口、监视窗口和寄存器窗口可能会显示具有不同位数的双精度浮点数。需要17个数字才能唯一标识特定的双精度浮点数。如果1.99999999999999舍入到14个数字,则会显示为2.0。请参阅以下链接以了解更多详细信息:。http://randomascii.wordpress.com/2013/02/07/float-precision-revisited-nine-digit-float-portability/ - Bruce Dawson

0

使用以下代码测试双精度浮点数的相等性:

public static bool AreEqual(double d1, double d2, double delta)
{
    return Math.Abs(d1 - d2) < delta;
}

当答案存在一些不确定性时,ε/δ值才是合适的。这通常是情况,但并非总是如此。请参见以下示例:http://randomascii.wordpress.com/2014/01/27/theres-only-four-billion-floatsso-test-them-all/如果确实需要一个ε值,那么就有一个棘手的问题,即要使用什么值。我在这里进行了深入讨论:http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ - Bruce Dawson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接