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;
The negative of zero is zero.
There's no such thing as negative zero. For a binary integer, setting the sign bit to 1 and all other bits to zero, you get the smallest negative value for that integer size. (Assuming signed numbers.) Negative zero is actually used in mathematical analysis, especially in limit calculations.
tAlthough one's complement can represent both positive and negative numbers, we are concerned only withunsigned representation here. SECTION10.5CHECKSUM299SolutionThe number21in binaryis10101 (it needs five bits). Wecan wrap the leftmost bit and add it tothe four rightmost bits. Wehave (0101+1)=0110 or6.
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:
The signed and unsigned integer types shall satisfy the constraints given in the C standard, section 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):
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:
the corresponding value with sign bit 0 is negated (sign and magnitude);
the sign bit has the value −(2^M) (two’s complement);
the sign bit has the value −(2^M − 1) (ones’ complement).
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. In the case of sign and magnitude and ones’ complement, if this representation is a normal value it is called a negative zero.
Consequently, the existence of negative zero is implementation defined.
If we proceed further in paragraph 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:
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:
3.9.1 Fundamental types [basic.fundamental] §3:
... The signed and unsigned integer types shall satisfy the constraints given in the C standard, section 5.2.4.2.1.
Later in C reference: 5.2.4.2.1 Sizes of integer types
... Forward references: representations of types (6.2.6)
and (still C): 6.2.6 Representations of types / 6.2.6.2 Integer types § 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.
For following lines, I will assume that the negative 0 was produced in a bitwise manner, on an implementation that supports it.
C++ standard does not speak at all of negative zeros, and C standard just say of them that their existence is implementation dependant. 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
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.
and in C++ 5.9 Relational operators [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.
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:
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.
So these two lines at least are perfectly defined and should produce 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
. A negative floating point 0 should be displayed with a minus sign, but nothing seems explicit for an integer negative value.
For the last 3 line involving relational and equality operators, there is nothing explicit in standard, so I would say it is implementation defined
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:
std::cout<<(1 << negzero)<<std::endl;
std::cout<<(1 >> negzero)<<std::endl;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With