是否可以区分 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
如果是负值,则取决于它们的存储方式。万一
- Two's complement –2,147,483,648 到 2,147,483,647
- One's complement –2,147,483,647 到 2,147,483,647
如果补数值-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
的一元运算符 -
,正如 三加五 只是二元运算符 +
应用于 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
}
}
当然,如果确实发生这种情况,除了直接内存操作之外,这两个值仍将以完全相同的方式工作。
我知道整数值 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
如果是负值,则取决于它们的存储方式。万一
- Two's complement –2,147,483,648 到 2,147,483,647
- One's complement –2,147,483,647 到 2,147,483,647
如果补数值-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
的一元运算符 -
,正如 三加五 只是二元运算符 +
应用于 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
}
}
当然,如果确实发生这种情况,除了直接内存操作之外,这两个值仍将以完全相同的方式工作。