Is the result of bitwise operations on signed integer types clearly defined?

Consider this code:

using integer = int; // or any other fundamental integral type using unsigned_integer = typename std::make_unsigned<integer>::type; constexpr integer bits = std::numeric_limits<unsigned_integer>::digits; integer value = -42; // or any value integer mask = static_cast<integer>(1)<<static_cast<integer>(bits-1); bool result_and = value & mask; bool result_or = value | mask; bool result_xor = value ^ mask; 

I am wondering how well these operations are defined in accordance with the standard. Do I have a guarantee to get the same results on all architectures? I'm sure I am working on a sign bit on all architectures, where is this sign bit 0 for positive numbers and 1 for negative numbers?

+7
c ++ standards bit-manipulation c ++ 14 signed
source share
3 answers

The results of bitwise, bitwise, or bitwise xor are currently unproven in the standard, in particular, the term bitwise is never defined. We have a bug report of 1857: Additional questions about bits that cover this issue and says:

The specification of bitwise operations in 5.11 [expr.bit.and], 5.12 [expr.xor], and 5.13 [expr.or] uses the term undefined "bitwise" to describe operations, without indicating whether it is a representation of a value or an object that is in line of sight.

Part of resolving this may be to define a β€œbit” (which is currently currently undefined in C ++) as a value of a given power of 2.

and permission was:

CWG decided to reformulate the description of operations themselves to avoid references to bits, separating more issues of determining the "bit", etc. for the 1943 issue for further consideration.

This led to the consolidated defect report 1943: Undefined bit value .

The result of the left shift of the signed type will depend on the underlying view. This can be seen from bug report 1457: undefined behavior at the left offset , which made it well-defined for a left shift in the sign bit and says:

The current edition of paragraph 5.8 [expr.shift] 2 makes it undefined to create the most negative integer of this type by shifting left (1) to the sign bit, although this is not unusual and works correctly on most (two-component) architectures :

... if E1 has a signed type and a non-negative value, and E1 β¨― 2E2 is represented in the result type, then this is the resulting value; otherwise, the behavior is undefined.

As a result, this method cannot be used in constant expression, which will break a significant amount of code.

Noting that the emphasis on the application works correctly on the majority (two-component). Thus, it depends on the underlying representation, for example, on a binary complement.

+3
source share

Regarding left and right shift operators from the standard C ++ 5.8 section:

The behavior is undefined if the right operand is negative or greater than or equal to the bit length of the advanced left operand.

He then says that the left shift operator E1 <E2 leads to undefined behavior when all of the following conditions are true:

  • The left operand has a signed type.
  • Either the left operand has a negative value, or has a non-negative value, so that E1 Γ— 2 ^ E2 is not represented in the resulting type.

Also, with respect to the right-shift operator E1 β†’ E2, the behavior is implementation-dependent if the left operand is of subscription type and negative value.

The bitwise operators AND, XOR, and OR are well defined for all integral types. This is indicated in sections 5.11, 5.12 and 5.13, respectively.

However, we note that the representation of signed integral values ​​can be either two-component, or complementary, or symbolic. However, most compilers use the Two Supplement view. These include gcc, VC ++, icl, and Clang.

+1
source share

Operators & , | and ^ are bitwise and process individual bits, so they will do what you wrote: apply mask .

The operator on the left side is << little more complicated. This will lead to undefined behavior if you shift the negative value or shift 1 to sign the bit position or further.

static_cast<integer>(1)<<static_cast<integer>(bits-1);

It seems you shift 1 to sign the bit position there, and this behavior is undefined.

+1
source share

All Articles