Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are the result of bitwise operations on signed integral types well-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 are these operations defined according to the standard. Do I have the guarantee to end up with the same results on all architectures? I am sure to operate on the sign bit on all architectures where this sign bit is 0 for positive numbers and 1 for negative numbers?

like image 645
Vincent Avatar asked Oct 21 '15 05:10

Vincent


People also ask

Which data types are bitwise operators mostly used on?

Bitwise Operators are used for manipulating data at the bit level, also called bit level programming. Bitwise operates on one or more bit patterns or binary numerals at the level of their individual bits. They are used in numerical computations to make the calculation process faster.

What are the advantages of using bitwise operations?

Bitwise operations are incredibly simple and thus usually faster than arithmetic operations. For example to get the green portion of an rgb value, the arithmetic approach is (rgb / 256) % 256 . With bitwise operations you would do something as (rgb >> 8) & 0xFF .

How does bitwise work inclusive?

The | (bitwise inclusive OR) operator compares the values (in binary format) of each operand and yields a value whose bit pattern shows which bits in either of the operands has the value 1 . If both of the bits are 0 , the result of that bit is 0 ; otherwise, the result is 1 .

What is bitwise inclusive operator?

The bitwise inclusive OR operator ( | ) compares each bit of its first operand to the corresponding bit of its second operand. If either bit is 1, the corresponding result bit is set to 1. Otherwise, the corresponding result bit is set to 0. Both operands to the operator must have integral types.


2 Answers

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

The specification of the bitwise operations in 5.11 [expr.bit.and], 5.12 [expr.xor], and 5.13 [expr.or] uses the undefined term “bitwise” in describing the operations, without specifying whether it is the value or object representation that is in view.

Part of the resolution of this might be to define “bit” (which is otherwise currently undefined in C++) as a value of a given power of 2.

and the resolution was:

CWG decided to reformulate the description of the operations themselves to avoid references to bits, splitting off the larger questions of defining “bit” and the like to issue 1943 for further consideration.

Which resulted in a consolidated defect report 1943: Unspecified meaning of “bit”.

The result of left shifting a signed type is going to depend on the underlying representation. We can see this from the defect report 1457: Undefined behavior in left-shift which made it well defined to left shift into the sign bit and says:

The current wording of 5.8 [expr.shift] paragraph 2 makes it undefined behavior to create the most-negative integer of a given type by left-shifting a (signed) 1 into the sign bit, even though this is not uncommonly done and works correctly on the majority of (twos-complement) architectures:

...if E1 has a signed type and non-negative value, and E1 ⨯ 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

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

Noting the emphasis on the statement works correctly on the majority of (twos-complement) architectures. So it is dependent on the underlying representation for example twos-complement.

like image 150
Shafik Yaghmour Avatar answered Sep 27 '22 19:09

Shafik Yaghmour


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

The behavior is undefined if the right operand is negative, or greater than or equal to the length in bits of the promoted left operand.

Then it says that the left shift operator E1 << E2 results in an undefined behavior when all of the following conditions are satisfied:

  • The left operand has a signed type.
  • Either the left operand has a negative value or it has a non-negative value such that E1 × 2^E2 is not representable in the resulting type.

Also regarding the right shift operator E1 >> E2, the behavior is implementation-dependent if the left operand has signed type and a negative value.

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

However, note that the the representation of signed integral values can be either Two's complement, Ones' complement or signed magnitude. Most compilers use the Two's complement representation though. These include gcc, VC++, icl and Clang.

like image 39
Hadi Brais Avatar answered Sep 27 '22 18:09

Hadi Brais