能否区分0和-0?

93

我知道整数值0-0本质上是相同的。但是,我想知道是否有可能区分它们。

例如,如何知道变量是否被赋值为-0

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

int num = -0;
int additinon = 5;

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

存储在内存中的值-00是否完全相同?


9
对于整数,没有区别。 - Maroun
15
这取决于具体的实现方式,但对于使用二进制补码来表示 int 的实现(目前最常见的情况),数字 0-0 在位表示上是完全相同的。 - Mankarse
11
在二进制补码机器中,位级别上没有区别。 - Marco A.
18
“in binary”是什么意思?实际上,有些二进制编码确实区分-0和0。例如,符号与大小编码。 - Benjamin Lindley
8
@VirtualSnake没错,我们正在谈论int。请参阅补码编码 - CiaPan
显示剩余6条评论
7个回答

114

这取决于您的目标计算机。

在使用 二进制补码 表示整数的计算机上,0-0 之间在位级别上没有任何区别(它们具有相同的表示)。

如果您的计算机使用了 一进制补码,那么肯定会有区别。

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

显然,我们谈论的是使用本地支持,x86系列处理器具有对带符号数的二进制补码表示的本地支持。使用其他表示法确实是可能的,但可能效率较低,需要更多指令。

(正如JerryCoffin所指出的那样:即使反码因历史原因而被认为是过时的,带符号数和数表示法仍然相当普遍,并且确实对正负零有单独的表示)


6
不太可能引起关注。我会很惊讶如果有人费力把C++编译器移植到这样的机器上去输出程序。 - Benjamin Lindley
1
我同意Benjamin的观点,历史上有一些使用它的机器,但现在我不知道有哪些生产机器在使用它。然而,了解它并将其牢记于心总是很好的。 - Marco A.
4
一补数在UNISYS 2200系统中仍在使用。 - phuclv
2
我从未考虑过补码要求——标准是否确实保证了 0-0不同的? 我真的希望它的行为更像是允许两个相同值的位表示,而你的程序可以使用任何一个。 - user1084944
8
即使存在负零表示,标准也不能保证使用表达式-0(即将整数常量0应用一元运算符-得到的结果)进行赋值或初始化会得到一个负零表示。不管具体表示方式如何,标准从未说过0-0在数学上是不同的值,只是可能存在一种负零位模式。如果存在这种模式,则仍然表示相同的数值0。 - Steve Jessop
显示剩余4条评论

15

对于一个使用几乎普遍的“二进制补码”表示法的整数int0-0的表示方式是相同的。(在其他数字表示法中,例如IEEE 754浮点数,它们可能不同。)


10
假设采用二进制补码表示法。 - Marco A.

14

让我们从用二进制补码表示0开始(当然还有许多其他的系统和表示方法,这里我只是参考了这个特定的方法),假设使用8位,那么0为:

0000 0000

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

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

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

但请注意,在1's complement中,有符号的0是0000 0000,而-0是1111 1111。


2
可以告诉我为什么会有负评,以便我改进我的回答吗? - Maroun
1
虽然其他回答在技术上都是正确的,但你的回答很实用,并且提供了具体实现。很好。 - umlcat

9
我决定保留这个答案,因为C和C++的实现通常是密切相关的,但事实上它并没有遵循我认为它所遵循的C标准。重点在于C++标准没有规定这些情况会发生什么。还有一点需要注意的是,在现实世界中,非二进制补码表示法非常罕见,即使存在这种表示法,它们通常会隐藏差异,而不是将其暴露出来,让人们轻易地发现。
负零在整数表示中的行为在C++标准中的定义没有C标准那么严格。然而,在顶层[1.2]处,它确实引用了C标准(ISO/IEC 9899:1999)作为规范参考。
在C标准[6.2.6.2]中,负零只能是按位操作的结果,或者是已经存在负零的操作(例如,将负零乘以或除以一个值,或将负零加到零中)- 将一般零的值应用于一元减运算符,如您的示例中所示,因此保证会产生一般零。
即使在那些可能生成负零的情况下,也不能保证它们会在支持负零的系统上生成负零:

未指定这些情况是否实际上会生成负零或一般零,以及负零在存储在对象中时是否变为一般零。

因此,我们可以得出结论:没有可靠的方法来检测这种情况。即使不考虑非二进制补码表示在现代计算机系统中非常罕见的事实。
至于C++标准,它没有提到“负零”这个术语,并且对有符号数和反码表示法的细节几乎没有讨论,除了指出[3.9.1段7]它们是允许的。

一般而言,C语言中某些事情是真实的/必须的,并不意味着在C++中也是如此。虽然C是规范性参考,但C++主要是在标准头文件的内容方面参照C标准,而整数类型的定义不是其中之一。然而,负零的产生没有保证的方式意味着你所得出的结论仍然是正确的,即使该表示存在,也没有确定的方法进行算术运算来生成它。 - Steve Jessop
那么为什么C++标准在这类事情上没有提供太多细节呢? - Random832
1
个人观点而已,我认为如果考虑到投票C++标准的人数可以被称为“个人”的话 :-) 如果要参考C标准的定义的话,那么它应该做好它的本职工作,不包含任何细节,就像在其他一些情况下所做的那样。 - Steve Jessop
“C++是一种通用的编程语言,基于ISO/IEC 9899:1999编程语言-C中描述的C编程语言(以下简称C标准)。[1.1第2段]是否具有规范性含义?我认为这旨在普遍地将C标准纳入任何不被C++标准特别覆盖的内容。” - Random832
@Random832 不是的。这只是一个历史注释(例如,C++中没有_Bool_Complex、指定初始化程序或复合字面量)。当需要时,C++标准知道如何并入C标准,例如[basic.fundamental]/p3:“带符号和无符号整数类型应满足C标准第5.2.4.2.1节中给出的约束条件。” - T.C.

8
如果您的计算机对-0+0有不同的表示方式,那么memcmp函数将能够区分它们。
如果存在填充位,除了零以外的值可能会有多个表示方式。

5
在C++语言规范中,不存在所谓的“负零”int。
这两个词仅仅意味着对0应用一元运算符“-”,就像“三加五”只是对3和5应用二元运算符“+”一样。
如果存在特殊的“负零”,那么补码(整数类型的最常见表示)将无法表示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
    }
}

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


3
实际上,语言不强制存在并不意味着它强制不存在。提示:它既不强制存在也不强制不存在。 - Deduplicator
2
@去重器,差不多吧。所谓“用C ++语言”,指的是“用C ++语言规范”。由于规范中也没有提到froobinator,因此我可以说“C ++没有froobinator”而不会有太多歧义。我认为这很清楚,但我会改进它。 - Paul Draper
1
语言规范也没有提到独角兽。 - ypercubeᵀᴹ

3
为了简化问题,我发现将其直观化会更容易理解。
类型 int(_32)使用 32位 存储。32位意味着有2 ^ 32 = 4294967296个唯一值。因此:

无符号int数据范围是 0到4,294,967,295

对于负值,它取决于它们如何存储。在情况下 反码中,值-0存在。

2
我没有投反对票,但是现在存储int不是32位的平台比使用一补数的平台更受欢迎。 - Maciej Piechotka

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