在C语言中比较浮点数

17

我有一个打印为0.000000double,并尝试与0.0f进行比较,但未成功。这里为什么会有差异?最可靠的方法是确定你的double是否为零?


5
你想知道它是否确切为零,还是足够接近零以至于小数点后6位为0.000000?换句话说,如果它非零但足够接近零,对你是否很重要打印出来的结果与零相同? - CB Bailey
5
请展示一些代码,并解释你所说的“unsuccessfully”的含义。 - Greg Hewgill
4个回答

20

为了确定其是否足够接近零,以便在六位小数的情况下打印为0.000000,可以使用以下方法:

fabs(d) < 0.0000005

在一般情况下,处理浮点数计算中的小误差可能会变得非常复杂。

如果您想更好地了解自己得到的值,请尝试使用%g而不是%f进行打印。


假设您的编译器支持它,您应该使用DBL_MIN而不是任意常数。 DBL_MIN通常定义为2.2250738585072014e-308,比0.0000005小得多,可以帮助消除误报。 - Steve Mallam
6
如果DBL_MIN是可表示的最小浮点数,那么检查是否小于它有什么意义? - Greg Hewgill
2
@Greg: 实际上 DBL_MIN 是最小的规格化双精度值,实现可能支持比它更小的非规格化值。 - Steve Jessop
2
@Steve Mallam:是的,这取决于提问者如何打印它以及他们认为什么值应该被视为零以适合他们的目的。我假设精确到小数点后六位,尽管我没有明确说明,因此我的代码是这样的。DBL_MIN非常小,不多计算都有误差,都会非常接近。有时候,DBL_EPSILON乘以计算中数字的数量是很好的选择。使用%g,即使是精确的双精度值0.0也不会打印为0.000000,我认为任何值都不会。所以我不知道确切的问题是什么,这是我最好的猜测,%f - Steve Jessop
全员+1,你们都是对的:DBL_EPSILON就是我想要建议的值!抱歉——我猜我匆忙中发表了评论... - Steve Mallam

4

您可以使用范围表达式。比如 -0.00001 <= x <= 0.00001。


可以编译,但它不会做你想要的事情。尝试使用x=-0.00002;x=5e-6; - dmckee --- ex-moderator kitten
3
你想要的是“-0.00001小于等于x且x小于等于0.00001”。 - Keith Thompson

4
这是现代计算机浮点运算的根本问题。它们天生不精确,无法可靠地进行比较。例如,ML语言明确禁止实数类型的相等比较,因为它被认为太不安全了。此外,David Goldberg在这个主题上撰写了一篇优秀的(虽然有些冗长和数学导向)文章,请参见该文
编辑:tl;dr:你可能做错了。

1
此外,浮点数常被忽视的一个特性是非规格化数。这些数具有最小指数,但不适合于0.5-1范围内。这些数字比浮点数的FLT_MIN和双精度浮点数的DBL_MIN要低。使用阈值的常见错误是比较两个值或使用FLT_MIN/DBL_MIN作为限制。例如,如果您不知道非规格化数,这将导致不合逻辑的结果。请注意保留HTML标签。
bool areDifferent(float a, float b) {
    if (a == b) return false;  // Or also: if ((a - b) == FLT_MIN) 
    return true;
}


// What is the output of areDifferent(val, val + FLT_MIN * 0.5f) ?
// true, not false, even if adding half the "minimum value".

Denormals通常意味着计算性能下降。但是,您不能禁用它们,否则这样的代码仍可能产生除以零的浮点异常(如果启用)。
float getInverse(float a, float b) {
    if (a != b)
        return 1.0f / (a-b); // With denormals disabled, a != b can be true, but (a - b) can still be denormals, it'll rounded to 0 and throw the exception
    return FLT_MAX;
}

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