【问题标题】:.NET - Floating Point Comparison.NET - 浮点比较
【发布时间】:2011-08-19 20:36:02
【问题描述】:

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

double Num
{
get{ return Math.Round(num, 1); }
}

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

【问题讨论】:

  • FWIW:变量中的值不会改变从 0.2 到 0.19999999999(即,随着时间的推移,它不会泄漏或流失或丢失一些位)。事实是 0.2 根本无法在二进制浮点变量中精确表示,因此它被存储为 double 或 float 可以表示的最接近的可能值。这就是为什么你会得到像 0.199999999999 或 0.20000000001 这样的东西。

标签: .net comparison floating-point


【解决方案1】:

不,不能保证。

来自 MSDN - Math.Round:

此方法的行为遵循 IEEE 标准 754 第 4 节。这种四舍五入有时称为四舍五入或银行家四舍五入。它最大限度地减少由于在单个方向上持续舍入中点值而导致的舍入误差。

(强调我的)

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


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

例子改编自here:

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
}

推荐阅读:What Every Computer Scientist Should Know About Floating-Point Arithmetic

【讨论】:

    【解决方案2】:

    不管你是否相信,这都是预期的行为,并且符合某些 IEEE 标准。

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

    这并不是数字在内存中的降级 - 这是一个经过深思熟虑的步骤。如果您要比较浮点数,则应始终允许比较的任一侧有一个可接受的区域。您对 0.2 的表示接近于非常多的小数位数。这对您的应用程序是否足够好?它看起来很刺眼,但实际上是一个很小的错误。在比较双精度数和浮点数(与整数或彼此)时,您应该始终考虑可接受的精度,并接受预期结果两侧的范围。

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

    【讨论】:

    • 十进制不仅更大(128位),而且最重要的是非常ssslllooowww,因为它不支持硬件,所以每个操作都必须在软件中完成。
    【解决方案3】:

    变量不会自行改变。如果 a == b 在某个时间点,那么 a == b 会一直持续下去,直到您修改 a 或 b。

    您可能会遇到与浮点数据类型的可表示性相关的问题,但不清楚问题出在哪里。很明显,您当前的解决方案几乎肯定不是一个好主意。

    【讨论】:

    • 其实我已经看到它改变了。我将一个变量设置为 0.2,一段时间后它更改为 0.1999999(在调试模式下)。但是你可能会说它没有改变,这是浮点表示的问题。当我在一段时间后查看同一个变量时,它可能又是 0.2。
    • 不,你从未见过它改变。变量不会自行改变。从那一天开始,我的世界就崩溃了,我开始寻找新的职业。
    • 变量不会改变,当然也不会来回改变。可能发生的情况是您的调试器可能会在不同的地方以不同的精度显示数字。例如,内存窗口、本地窗口、监视窗口和寄存器窗口可能会显示具有不同位数的双精度数。唯一标识一个特定的双精度需要 17 位数字。如果 1.99999999999999 被四舍五入到 14 位,它将显示为 2.0。有关详细信息,请参阅此:。 randomascii.wordpress.com/2013/02/07/…
    【解决方案4】:

    使用这样的代码来测试 double 是否相等:

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

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-21
    • 1970-01-01
    • 1970-01-01
    • 2021-07-10
    • 2011-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多