是否可以区分 0 和 -0?

Is it possible to differentiate between 0 and -0?

我知道整数值 0-0 本质上是一样的。 但是,我想知道是否可以区分它们。

例如,我如何知道变量是否已分配 -0

bool IsNegative(int num)
{
    // How ?
}

int num = -0;
int additinon = 5;

num += (IsNegative(num)) ? -addition : addition;

-0在内存中的保存方式是否与0完全相同?

对于int(在几乎通用的“2 的补码”表示中),0-0 的表示是相同的。 (对于其他数字表示,它们可能不同,例如 IEEE 754 浮点数。)

这取决于您的目标机器。

在对整数使用 2's complement representation 的机器上,0-0 在位级别 上没有区别(它们有相同的表示法)

如果你的机器使用 one's complement,你肯定可以

0000 0000   -> signed   0 
1111 1111   -> signed   −0

显然我们正在谈论使用 原生支持,x86 系列处理器原生支持有符号数的二进制补码表示。使用其他表示绝对是可能的,但可能效率较低并且需要更多指令。

(正如 JerryCoffin 还指出的那样:即使主要出于历史原因考虑了一个补码,signed magnitude representations 仍然相当普遍,并且确实有单独的负零和正零表示)

让我们从用 2 的补码表示 0 开始(当然还有许多其他系统和表示法,这里我指的是这个特定的),假设 8 位,零是:

0000 0000

现在让我们翻转所有位并加 1 得到 2 的补码:

1111 1111 (flip)
0000 0001 (add one)
---------
0000 0000

我们得到了0000 0000,这也是-0的表示。

但要注意1的补码,有符号0是0000 0000,-0是1111 1111

为了简化,我发现它更容易可视化。

类型int(_32)以32位存储。 32 位表示 2^32 = 4294967296 唯一值。因此:

unsigned int数据范围是0到4,294,967,295

如果是负值,则取决于它们的存储方式。万一

如果补数值-0存在。

如果您的机器对 -0+0 有不同的表示,那么 memcmp 将能够区分它们。

如果存在填充位,实际上也可能有多种表示非零的值。

我决定保留这个答案,因为 C 和 C++ 实现通常密切相关,但实际上它并不像我认为的那样遵循 C 标准。重点仍然是 C++ 标准没有指定对于这些情况会发生什么。同样重要的是,非二进制补码表示在现实世界中极为罕见,即使它们确实存在,它们在许多情况下也常常隐藏差异,而不是将其暴露为人们很容易期望发现的东西。


负零在其存在的整数表示中的行为在 C++ 标准中没有像在 C 标准中那样严格定义。但是,它确实引用了 C 标准 (ISO/IEC 9899:1999) 作为顶级 [1.2] 的规范参考。

在C标准[6.2.6.2]中,负零只能是按位运算的结果,或者负零已经存在的运算(例如,将负零乘以或除以一个值,或者将负零添加到零) - 因此,如您的示例所示,将一元减号运算符应用于正常零的值,因此保证会产生正常零。

即使在 可以 生成负零的情况下,也不能保证它们会生成负零,即使在支持负零的系统上也是如此:

It is unspecified whether these cases actually generate a negative zero or a normal zero, and whether a negative zero becomes a normal zero when stored in an object.

因此,我们可以得出结论:不,没有可靠的方法来检测这种情况。即使不是因为非二进制补码表示在现代计算机系统。

C++ 标准,就其本身而言,没有提及术语 "negative zero",并且很少讨论有符号大小和补码表示的细节,除了注释 [3.9.1 第 7 段] ] 他们是被允许的。

在C++语言规范中,没有负零这样的int。

这两个词的唯一含义是应用于 0 的一元运算符 -,正如 三加五 只是二元运算符 + 应用于 35

如果有一个不同的负零,二进制补码(整数类型的最常见表示)将不足以表示 C++ 实现,因为没有办法表示两种形式的零。


相比之下,浮点数(遵循 IEEE)具有单独的正零和负零。它们可以被区分,例如,当用它们除以 1 时。正零产生正无穷大;负零产生负无穷大。


但是,如果 int 0(或任何 int,或任何其他类型的任何其他值)碰巧有不同的内存表示,您可以使用 memcmp 发现:

#include <string>

int main() {
    int a = ...
    int b = ...
    if (memcmp(&a, &b, sizeof(int))) {
        // a and b have different representations in memory
    }
}

当然,如果确实发生这种情况,除了直接内存操作之外,这两个值仍将以完全相同的方式工作。