37

IEEE 浮点数分配了一个位来指示符号,这意味着从技术上讲,您可以使用不同的二进制表示零(+0 和 -0)。有没有我可以做的算术运算,例如在 C 中导致负零浮点值?

这个问题的灵感来自另一个问题,它质疑您是否可以安全地比较0.0fusing ==,我进一步想知道是否还有其他方法来表示零,这会导致float1 == 0.0f看似完全相等的值中断。

[编辑] 请不要评论比较浮动是否相等的安全性!我并不是要添加到那个溢出的重复问题中。

4

10 回答 10

30

根据标准,负零存在,但它等于正零。对于几乎所有目的,两者的行为方式相同,并且许多人认为否定的存在是实现细节。然而,有些函数的行为完全不同,即除法和atan2

#include <math.h>
#include <stdio.h>

int main() {
    double x = 0.0;
    double y = -0.0;
    printf("%.08f == %.08f: %d\n", x, y, x == y);
    printf("%.08f == %.08f: %d\n", 1 / x, 1 / y, 1 / x == 1 / y);
    printf("%.08f == %.08f: %d\n", atan2(x, y), atan2(y, y), atan2(x, y) == atan2(y, y));
}

这段代码的结果是:

0.00000000 == -0.00000000: 1
1.#INF0000 == -1.#INF0000: 0
3.14159265 == -3.14159265: 0

这意味着代码可以正确处理某些限制,而无需显式处理。对于接近极限的值依赖这个特性并不是一个好主意,因为一个简单的计算错误可能会改变符号并使值远离正确,但如果你避免计算,你仍然可以利用它改变标志。

于 2011-02-23T19:45:30.117 回答
16

有没有我可以做的算术运算,例如在 C 中导致负零浮点值?

当然:

float negativeZero = -10.0e-30f * 10.0e-30f;

乘法的数学精确结果不能表示为浮点值,因此它四舍五入到最接近的可表示值,即-0.0f.

IEEE-754 标准很好地定义了负零的语义;在算术表达式中,它的行为与零的行为不同的唯一真正可观察的方式是,如果你除以它,你将得到一个不同的无穷大符号。例如:

1.f /  0.f --> +infinity
1.f / -0.f --> -infinity

比较和加法和减法与-0.f它们的结果相同+0.f(在默认舍入模式下)。乘法可以保留零的符号,但如前所述,它通常是不可观察的。

一些数学库函数的行为可能会因零的符号而异。例如:

copysignf(1.0f, 0.0f) -->  1.0f
copysignf(1.0f,-0.0f) --> -1.0f

这在复杂函数中更常见:

csqrtf(-1.0f + 0.0f*i) --> 0.0f + 1.0f*i
csqrtf(-1.0f - 0.0f*i) --> 0.0f - 1.0f*i

但是,一般来说,您不必担心负零。

于 2011-02-23T19:50:22.963 回答
8

是的,零可以签名,但标准要求正零和负零测试相等

于 2011-02-23T19:28:32.597 回答
5

有几个简单的算术运算会导致负零答案(至少在我测试过的 i386/x64/ARMv7/ARMv8 系统上):

  • -1 * 0
  • 0 / -1

当我写一个优化器来简化算术表达式时,这些让我大吃一惊。如果 b 恰好为负(正确答案为 -0),则将“ a = b * 0 ”优化为“ a = 0 ”将导致错误答案 (+0)。

于 2016-04-05T09:37:59.043 回答
2

是的, float确实有一个负零,但是没有,在比较浮点值时您不必担心这一点。

浮点运算被定义为在特殊情况下正确工作。

于 2011-02-23T19:29:13.083 回答
1

是的,floats 与其他 IEEE 浮点类型一样具有负零,例如double(在具有 IEEE 浮点的系统上)。Octave中有一个如何创建它们示例;相同的操作在 C 中起作用。但是,==运算符将 +0 和 -0 视为相同,因此负零不会破坏这种类型的比较。

于 2011-02-23T19:28:30.727 回答
1

是的,您可以有一个 +0 和 -0 并且它们是不同的位模式(应该无法通过相等性测试)。你不应该使用 == 和浮点数,当然不是 IEEE 浮点数。< 或 > 都可以。关于这个主题还有很多其他的 SO 问题和讨论,所以我不会在这里讨论。

于 2011-02-23T19:33:34.580 回答
0

float1 == 0.0f从来都不是一个真正安全的比较。

如果你有类似的东西

float x = 0.0f;
for (int i = 0; i < 10; i++) x += 0.1f;
x -= 1.0f;
assert (x == 0.0f);

即使它看起来应该是 0,它也会失败。

于 2011-02-23T19:29:05.670 回答
0

使用浮点数进行相等比较时应小心谨慎。请记住,您正在尝试在二进制系统中表示十进制值。

检查浮点值是否等于 0 是否安全?

如果您必须比较浮点值,我建议您使用某种您可以接受的容差,float1 <= toleranceVal && float1 >= toleranceVal2或者乘以十倍并转换为整数。 if (!(int)(float1 * 10000)) { .. some stuff .. }

于 2011-02-23T19:41:28.407 回答
0

-lm 具有 signbit() 函数可用于指示值是否为负(包括 -0)

于 2013-02-04T00:04:31.943 回答