【问题标题】:Is it possible to differentiate between 0 and -0?是否可以区分0和-0?
【发布时间】:2015-07-09 20:30:06
【问题描述】:

我知道整数值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 具有相同的按位表示。
  • 在 2 的补码机器上,比特级没有区别。
  • @VirtualSnake:“二进制”是什么意思?事实上,在二进制编码中 -0 和 0 之间的区别。例如,符号和幅度。
  • @VirtualSnake 没错,我们说的是int。见Ones' complement encoding

标签: c++ int zero negative-number


【解决方案1】:

这取决于您的目标机器。

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

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

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

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

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

【讨论】:

  • @TobiMcNamobi:不太可能被关注。如果有人费心移植 C++ 编译器来为这样的机器生成输出,我会感到惊讶。
  • 我同意本杰明的观点,historically 有机器使用它,但现在我碰巧不知道使用它的生产机器。尽管如此,知道并牢记在心总是好的。
  • @TobiMcNamobi 的补码仍在 UNISYS 2200 系统中使用 stackoverflow.com/a/12277974/995714 stackoverflow.com/q/6971886/995714
  • 我从来没有看过一个人的补码要求——标准是否真的保证0-0不同的?老实说,我本来希望它的行为更像是允许相同值的两位表示,并且您的程序可以使用任何感觉。
  • @Hurkly:不,即使存在负零表示,标准也不保证使用表达式-0进行赋值或初始化,也就是说应用一元@987654333的结果@运算符对整数常量0,是一个负零表示。无论表示方式如何,该标准都不会说 0-0 在数学上是不同的值,只是可能存在负零位模式。如果有,它仍然代表相同的数值,0。
【解决方案2】:

让我们从用 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。

【讨论】:

  • 我可以知道为什么投反对票以改善我的答案吗?
  • 虽然大多数其他答案在技术上是正确的,但您的答案是实用的,并提供了一个实现。很好。
【解决方案3】:

我决定不回答这个问题,因为 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++ 标准对这类事情的细节如此之少?
  • 个人品味,我认为,如果对 C++ 标准投票的人数可以被认为是“个人的” :-) 如果它要遵循 C 标准的定义,那么它可以很好地完成它并包含 no 细节,就像在其他一些情况下一样。
  • 是否“C++ 是一种基于 ISO/IEC 9899:1999 编程语言 — C(以下简称 C 标准)中描述的 C 编程语言的通用编程语言。” [1.1 para 2]有什么规范意义吗?我认为这通常是为了将 C 标准纳入 C++ 标准未明确覆盖的任何内容。
  • @Random832 不。这只是一个历史记录(例如,在 C++ 中没有 _Bool_Complex 或指定的初始化程序或复合文字)。 C++ 标准知道如何在需要时合并 C 标准 - 例如,[basic.fundamental]/p3:“有符号和无符号整数类型应满足 C 标准第 5.2.4.2.1 节中给出的约束。”
【解决方案4】:

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

这两个词的唯一含义是应用于0 的一元运算符-,就像三加五 只是应用于3 和@ 的二元运算符+ 987654325@.

如果有一个不同的负零,二进制补码(整数类型的最常见表示)将不足以表示 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
    }
}

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

【讨论】:

  • 实际上,语言不强制它存在并不意味着它强制它不存在。提示:两者都不要求。
  • @Deduplicator,有点像。 “在 C++ 语言中”,我的意思是,“在 C++ 语言规范中”。由于规范中也没有提到 froobinators,我可以说“C++ 没有 froobinators”而没有太多歧义。我认为这很清楚,但我会改进它。
  • 语言规范也没有提到独角兽。
【解决方案5】:

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

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

【讨论】:

    【解决方案6】:

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

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

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

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

    如果存在补码值-0。

    【讨论】:

    • 我没有投反对票,但int 不以 32 位存储的平台比现在的互补平台更受欢迎。
    【解决方案7】:

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

    【讨论】:

    • >> 假设一个 2 的补码表示
    猜你喜欢
    • 1970-01-01
    • 2016-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-28
    • 1970-01-01
    • 2021-09-16
    相关资源
    最近更新 更多