是否可以区分0和-0?


94

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

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

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

int num = -0;
int additinon = 5;

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

-0保存在内存中的值是否与完全相同0


9
对于整数,没有区别。
Maroun

14
这取决于实现,但是对于int以2的补码表示的实现(迄今为止最常见),0并且-0具有相同的按位表示。
Mankarse

11
在2的补码机上,位级别没有区别。
Marco A.

17
@VirtualSnake:“二进制”是什么意思?实际上,二进制编码的-0和0之间有区别的。例如,符号和大小。
本杰明·林德利

8
@VirtualSnake没错,我们正在谈论int。参见补码
CiaPan 2015年

Answers:


112

这取决于您要定位的计算机。

在对整数使用2的补码表示形式的机器上,和之间的比特级没有区别(它们具有相同的表示形式)0-0

如果您的机器使用补码,则绝对可以

0000 0000   -> signed0
1111 1111   -> signed   0

显然,我们在谈论使用本机支持,x86系列处理器对有符号数的二进制补码表示法具有本机支持。使用其他表示形式绝对是可能的,但可能效率较低,并且需要更多说明。

(正如杰里·科芬(JerryCoffin)还指出的那样:即使人们的补码主要是出于历史原因,但有符号的幅度表示仍然相当普遍,并且对于负零和正零也有单独的表示。)


6
@TobiMcNamobi:不太可能被关注。如果有人不愿意移植C ++编译器为此类机器生成输出,我会感到惊讶。
本杰明·林德利

1
我同意本杰明(Benjamin)的观点,历史上一直有使用它的机器,但是如今我不知道有使用它的生产机器。尽管如此,了解并牢记始终是一件好事。
Marco A.

4
@TobiMcNamobi的补码仍在UNISYS 2200系统中使用stackoverflow.com/a/12277974/995714 stackoverflow.com/q/6971886/995714
phuclv 2015年

2
我从没看过一个人的补充要求-标准实际上保证了0-0不同吗?老实说,我希望它表现得更像允许使用相同值的两个位表示形式,并且您的程序可以使用任何感觉到的形式。

8
@Hurkly:不,即使存在负零表示,标准也不保证使用表达式进行赋值或初始化-0,也就是说,将一元运算-符应用于整数常量的结果0是负零表示。不管表示形式如何,该标准从不说0并且-0在数学上是不同的值,只是可能存在负零位模式。如果有,它仍然是相同的数值,0
史蒂夫·杰索普

14

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


9
>>假设补码为2
Marco A.

12

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

0000 0000

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

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

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

但请注意,在1的补码中,有符号0为0000 0000,但-0为1111 1111。


1
我能知道为什么要投票否决以改善我的答案吗?
Maroun

1
尽管其他大多数答案在技术上都是正确的,但您的答案是实用的,并且可以实现。好。
umlcat 2015年

9

由于C和C ++实现通常密切相关,所以我决定不回答这个问题,但是实际上,它并没有像我认为的那样遵循C标准。关键在于,C ++标准没有规定在此类情况下会发生什么。同样重要的是,在现实世界中,非二进制补码表示形式极为罕见,即使存在,它们在很多情况下也常常掩盖差异,而不是将其暴露为人们很容易发现的事物。


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

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

即使在可能产生负零的情况下,即使在支持负零的系统上也无法保证它们会:

尚不确定这些情况是实际生成负零还是正常零,以及存储在对象中时负零是否变为正常零。

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

就C ++标准而言,它没有提及“负零”一词,除了指出[3.9.1第7段]允许使用符号幅度和补码表示的细节外,很少讨论。


通常不,在C 正确/必需的事实并不一定意味着在C ++中正确/必需。C是一个规范性引用,这一事实意味着C ++在各种事物(主要是标准标头的内容)中都引用了C标准,但是整数类型的定义并不是其中之一。但是,由于缺乏保证产生负零的方法,这意味着您得出的结论仍然正确,即使存在表示形式,也没有确定的方法可以使用算术生成一个。
史蒂夫·杰索普

那么,为什么C ++标准在这样的事情上没那么详细呢?
2015年

1
我个人的品味,如果对C ++标准投票的人数可以被认为是“个人的” :-)但是,如果要遵循C标准的定义,那么它可以做得很好。并且不包含任何细节,就像在其他情况下一样。
史蒂夫·杰索普

是否“ C ++是基于ISO / IEC 9899:1999编程语言-C(以下称为C标准)中所述的C编程语言的通用编程语言”。[1.1第2段]有任何规范意义?我认为,这通常是为了将C标准并入没有被C ++标准明确覆盖的所有内容。
2015年

@ Random832不。这只是一个历史记录(例如,在C ++中没有_Bool_Complex或没有指定的初始值设定项或复合文字)。C ++标准知道如何在需要时合并C标准,例如[basic.fundamental] / p3:“有符号和无符号整数类型应满足C标准第5.2.4.2.1节中给出的约束。”
TC

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 2015年

2
@Deduplicator,有点。“在C ++语言中”是指“在C ++语言规范中 ”。由于规范中也没有提到frobinator,因此我可以说“ C ++没有frobinator”。我以为这很清楚,但我会加以改善。
Paul Draper 2015年

1
语言规范也没有提到独角兽。
ypercubeᵀᴹ

2

为简化起见,我发现它更易于可视化。

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

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

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

一个人的补码值的情况下-0存在。


2
我还没有投票赞成,但int如今没有以32位存储的平台比带有补码的平台更受欢迎。
Maciej Piechotka
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.