简体   繁体   English

一个补码架构上的负零行为?

[英]Behaviour of negative zero on a one's complement architecture?

Consider the following code on a one's complement architecture: 在一个补码架构上考虑以下代码:

int zero = 0;
int negzero = -0;
std::cout<<(negzero < zero)<<std::endl;
std::cout<<(negzero <= zero)<<std::endl;
std::cout<<(negzero == zero)<<std::endl;
std::cout<<(~negzero)<<(~zero)<<std::endl;
std::cout<<(1 << negzero)<<std::endl;
std::cout<<(1 >> negzero)<<std::endl;
  • What output would the code produce? 代码会产生什么输出?
  • What lines are defined by the standard, what lines are implementation dependent, and what lines are undefined behaviour? 标准定义了哪些行,哪些行依赖于实现,哪些行是未定义的行为?

Based on my interpretation of the standard: 根据我对标准的解释:

The C++ standard in §3.9.1/p3 Fundamental types [basic.fundamental] actually throws the ball in the C standard: §3.9.1/ p3中的C ++标准基本类型[basic.fundamental]实际上在C标准中抛出球:

The signed and unsigned integer types shall satisfy the constraints given in the C standard, section 5.2.4.2.1. 有符号和无符号整数类型应满足C标准5.2.4.2.1节中给出的约束。

Now if we go to ISO/IEC 9899:2011 section 5.2.4.2.1 it gives as a forward reference to §6.2.6.2/p2 Integer types ( Emphasis Mine ): 现在,如果我们转到ISO / IEC 9899:2011第5.2.4.2.1节,它将作为§6.2.6.2/ p2整数类型强调矿 )的前向参考:

If the sign bit is zero, it shall not affect the resulting value. 如果符号位为零,则不应影响结果值。 If the sign bit is one, the value shall be modified in one of the following ways: 如果符号位为1,则应以下列方式之一修改该值:

  • the corresponding value with sign bit 0 is negated (sign and magnitude); 符号位0的相应值被否定(符号和幅度);

  • the sign bit has the value −(2^M) (two's complement); 符号位的值为 - (2 ^ M)(二进制补码);

  • the sign bit has the value −(2^M − 1) (ones' complement). 符号位的值为 - (2 ^ M - 1)(1'补码)。

Which of these applies is implementation-defined , as is whether the value with sign bit 1 and all value bits zero (for the first two), or with sign bit and all value bits 1 (for ones' complement), is a trap representation or a normal value. 这些适用中的哪一个是实现定义的 ,将符号位1和所有值位0(对于前两个),或者符号位和所有值位1(对于1'补码)的值是否是陷阱表示或正常值。 In the case of sign and magnitude and ones' complement, if this representation is a normal value it is called a negative zero. 在符号和幅度以及1'补码的情况下,如果该表示是正常值,则称为负零。

Consequently, the existence of negative zero is implementation defined. 因此,负零的存在是实现定义的。

If we proceed further in paragraph 3: 如果我们在第3段继续进行:

If the implementation supports negative zeros, they shall be generated only by: 如果实现支持负零,则只能通过以下方式生成它们:

  • the &, |, ^, ~, <<, and >> operators with operands that produce such a value; &,|,^,〜,<<和>>运算符,其操作数产生这样的值;

  • the +, -, *, /, and % operators where one operand is a negative zero and the result is zero; +, - ,*,/和%运算符,其中一个操作数为负零,结果为零;

  • compound assignment operators based on the above cases. 基于上述情况的复合赋值算子。

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. 未指定这些情况是否实际产生负零或正常零,以及当存储在对象中时负零是否变为正常零。

Consequently, it is unspecified whether the related cases that you displayed are going to generate a negative zero at all. 因此,未指定您显示的相关案例是否会产生负零。

Now proceeding in paragraph 4: 现在进入第4段:

If the implementation does not support negative zeros, the behavior of the &, |, ^, ~, <<, and >> operators with operands that would produce such a value is undefined. 如果实现不支持负零,则具有将产生此类值的操作数的&,|,^,〜,<<和>>运算符的行为是未定义的。

Consequently, whether the related operations result in undefined behaviour, depends on whether the implementation supports negative zeros. 因此,相关操作是否导致未定义的行为取决于实现是否支持负零。

First of all, your first premise is wrong: 首先,你的第一个前提是错误的:

int negzero = -0;

should produce a normal zero on any conformant architecture. 应该在任何一致的架构上产生正常的零。

References for that were given in @101010's answer: 在@ 101010的回答中给出了参考文献:

3.9.1 Fundamental types [basic.fundamental] §3: 3.9.1基本类型[basic.fundamental]§3:

... The signed and unsigned integer types shall satisfy the constraints given in the C standard, section 5.2.4.2.1. ...有符号和无符号整数类型应满足C标准第5.2.4.2.1节中给出的约束。

Later in C reference: 5.2.4.2.1 Sizes of integer types 稍后在C参考:5.2.4.2.1整数类型的大小

... Forward references: representations of types (6.2.6) ...前向参考:类型表示(6.2.6)

and (still C): 6.2.6 Representations of types / 6.2.6.2 Integer types § 3 和(仍为C):6.2.6类型表示/ 6.2.6.2整数类型§3

If the implementation supports negative zeros, they shall be generated only by: 如果实现支持负零,则只能通过以下方式生成它们:

  • the &, |, ^, ~, <<, and >> operators with arguments that produce such a value; 带有产生这样一个值的参数的&,|,^,〜,<<和>>运算符;

  • the +, -, *, /, and % operators where one argument is a negative zero and the result is zero; +, - ,*,/和%运算符,其中一个参数为负零,结果为零;

  • compound assignment operators based on the above cases. 基于上述情况的复合赋值算子。

So negzero = -0 is not such a construct and shall not produce a negative 0. 所以negzero = -0 不是这样的结构, 不会产生负0。

For following lines, I will assume that the negative 0 was produced in a bitwise manner, on an implementation that supports it . 对于以下行,我将假设在支持它的实现上以负按位方式生成负0

C++ standard does not speak at all of negative zeros, and C standard just say of them that their existence is implementation dependant. C ++标准并不是所有的负零,而C标准只是说它们的存在是依赖于实现的。 I could not find any paragraph explicitly saying whether a negative zero should or not be equal to a normal zero for relational or equality operator. 我找不到任何段落明确说明负零是否应该等于关系或相等运算符的正常零。

So I will just cite in C reference : 6.5.8 Relational operators §6 所以我将引用C参考:6.5.8关系运算符§6

Each of the operators < (less than), > (greater than), <= (less than or equal to), and >= (greater than or equal to) shall yield 1 if the specified relation is true and 0 if it is false.92) The result has type int. 如果指定的关系为真,则每个运算符<(小于),>(大于),<=(小于或等于)和> =(大于或等于)将产生1,如果是,则为0 false.92)结果的类型为int。

and in C++ 5.9 Relational operators [expr.rel] §5 在C ++ 5.9关系运算符[expr.rel]§5

If both operands (after conversions) are of arithmetic or enumeration type, each of the operators shall yield true if the specified relationship is true and false if it is false. 如果两个操作数(转换后)都是算术或枚举类型,则每个运算符如果指定的关系为真,则应为true,如果为假,则为false。

My interpretation of standard is that an implementation may allow an alternate representation of the integer value 0 ( negative zero ) but it is still a representation of the value 0 and it should perform accordingly in any arithmetic expression, because C 6.2.6.2 Integer types § 3 says: 我对标准的解释是一个实现可能允许整数值0( 负零 )的替代表示,但它仍然是值0的表示,它应该在任何算术表达式中相应地执行,因为C 6.2.6.2整数类型§ 3说:

negative zeros[...] shall be generated only by [...] the +, -, *, /, and % operators where one argument is a negative zero and the result is zero 负零[...]只能由[...] +, - ,*,/和%运算符生成,其中一个参数为负零,结果为零

That means that if the result is not 0, a negative 0 should perform as a normal zero. 这意味着如果结果不为0,则负0应该作为正常零。

So these two lines at least are perfectly defined and should produce 1 : 所以这两行至少是完美定义的,应该产生1

std::cout<<(1 << negzero)<<std::endl;
std::cout<<(1 >> negzero)<<std::endl;

This line is clearly defined to be implementation dependant: 该行明确定义为依赖于实现:

std::cout<<(~negzero)<<(~zero)<<std::endl;

because an implementation could have padding bits. 因为实现可能有填充位。 If there are no padding bits, on a one's complement architecture ~zero is negzero , so ~negzero should produce a 0 but I could not find in standard if a negative zero should display as 0 or as -0 . 如果没有填充位,在一个补码架构上, ~zero negzero ,所以~negzero应该产生0但是如果负零应该显示为0-0我无法在标准中找到。 A negative floating point 0 should be displayed with a minus sign, but nothing seems explicit for an integer negative value. 应使用减号显示负浮点 0,但对于整数负值,似乎没有任何显式。

For the last 3 line involving relational and equality operators, there is nothing explicit in standard, so I would say it is implementation defined 对于涉及关系运算符和相等运算符的最后3行,标准中没有任何明确的含义,因此我认为它是实现定义的

TL/DR: TL / DR:

Implementation-dependent: 实现相关的:

std::cout<<(negzero < zero)<<std::endl;
std::cout<<(negzero <= zero)<<std::endl;
std::cout<<(negzero == zero)<<std::endl;
std::cout<<(~negzero)<<(~zero)<<std::endl;

Perfectly defined and should produce 1: 完美定义并应生成1:

std::cout<<(1 << negzero)<<std::endl;
std::cout<<(1 >> negzero)<<std::endl;

First of all one's complement architectures (or even distinguish negative zero) are rather rare, and there's a reason for that. 首先,一个补充架构(甚至可以区分负零)是相当罕见的,这是有原因的。 It's basically easier to (hardware-wise) add two's complement than one's complement. (硬件方面)添加两个补码比一个补码更容易。

The code you've posted doesn't seem to have undefined behavior or even implementation defined behavior, it should probably not result in negative zero (or it shouldn't be distinguished from normal zero). 您发布的代码似乎没有未定义的行为甚至是实现定义的行为,它可能不会导致负零(或者它不应该与正常的零区分开)。

Negative zeros should not be that easy to produce (and if you manage to do that it's implementation defined behavior at best). 负零不应该那么容易生成(如果你设法做到这一点,它最好是实现定义的行为)。 If it's a ones-complement architecture they would be produced by ~0 (bit-wise inversion) rather than -0 . 如果它是一个补码结构,它们将由~0 (逐位反转)而不是-0

The C++ standard is rather vague about the actual representation and requirements on the behavior of basic types (which means that the specification only deals with the actual meaning of the number). 关于基本类型行为的实际表示和要求,C ++标准相当含糊(这意味着规范只涉及数字的实际含义)。 What this means is that you basically are out of luck in relating internal representation of the number and it's actual value. 这意味着你基本上没有关联数字的内部表示和它的实际价值。 So even if you did this right and used ~0 (or whatever way is proper for the implementation) the standard still doesn't seem to bother with the representation as the value of negative zero is still zero. 因此,即使你做得正确并且使用了~0 (或者任何适合实现的方式),标准仍然似乎不会对表示感到烦恼,因为负零的值仍然为零。

#define zero (0)
#define negzero (~0)
std::cout<<(negzero < zero)<<std::endl;
std::cout<<(negzero <= zero)<<std::endl;
std::cout<<(negzero == zero)<<std::endl;
std::cout<<(~negzero)<<(~zero)<<std::endl;
std::cout<<(1 << negzero)<<std::endl;
std::cout<<(1 >> negzero)<<std::endl;

the three first lines should produce the same output as if negzero was defined the same as zero . 三个第一行应产生相同的输出,就好像negzero定义zero The third line should output two zeros (as the standard requires that 0 to be rendered as 0 without sign-sign). 第三行应输出两个零(因为标准要求0呈现为0而没有符号标记)。 The two last should output ones. 最后两个应该输出一个。

There are some hints (on how to produce negative zeros) that can be found in the C standard which actually mentions negative zero, but I don't think there's any mentions about they should compare less than normal zero. 有一些提示 (关于如何产生负零)可以在C标准中找到,实际上提到负零,但我认为没有任何提及他们应该比正常零更低。 The C-standard suggests that negative zero might not survive storage in object (that's why I avoided that in the above example). C标准表明负零可能无法在对象中存储(这就是为什么我在上面的例子中避免了这一点)。

The way C and C++ are related it's reasonable to think that a negative zero would be produced in the same way in C++ as in C, and the standard seem to allow for that. C和C ++相关的方式是合理的,认为在C ++中以与在C中相同的方式生成负零,并且标准似乎允许这样做。 While the C++ standard allows for other ways (via undefined behavior), but no other seem to be available via defined behavior. 虽然C ++标准允许其他方式(通过未定义的行为),但似乎没有其他方法可用于定义的行为。 So it's rather certain that if a C++ implementation is to be able to produce negative zeros in an reasonable way it would be the same as for a similar C implementation. 因此,相当肯定的是,如果C ++实现能够以合理的方式产生负零,那么它将与类似的C实现相同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM