something_clever
something_clever

Reputation: 846

Are bitwise operations defined and portable on signed fixed-width integers?

My understanding is that certain bitwise operations on the regular ol' short/int/long types are either implementation-dependent ( | & ~ >> ) or undefined ( << )

However, C99 introduced the fixed-width integer types and explicitly defines them to be two's-complement exact-bitness with no padding bits.

Does that mean that all the bitwise operations are well-defined and portable for those types among platforms that provide them?

For example, this Works on My Machine™, but is it guaranteed to work?

#include <inttypes.h>
#include <stdio.h>

int main() {
  uint16_t a = 0xffff;
  int16_t b = *(int16_t*)(&a);

  printf("%" PRId16 "\n", b);

  // Prints '-1'

  b <<= 4;

  printf("%" PRId16 "\n", b); 

  // Prints '-16'

  return 0;
}

Upvotes: 2

Views: 149

Answers (1)

dbush
dbush

Reputation: 223739

Using the fixed width types does not guarantee protection from undefined behavior related to bit shifting. Section 7.20.1.1 of the C standard regarding exact width integer types states:

1 The typedef name intN_t designates a signed integer type with width N, no padding bits, and a two’s complement representation. Thus, int8_t denotes such a signed integer type with a width of exactly 8 bits.

2 The typedef name uintN_t designates an unsigned integer type with width N and no padding bits. Thus, uint24_t denotes such an unsigned integer type with a width of exactly 24 bits.

3 These types are optional. However, if an implementation provides integer types with widths of 8, 16, 32, or 64 bits, no padding bits, and (for the signed types) that have a two’s complement representation, it shall define the corresponding typedef names.

Nothing here mentions special treatment regarding the behavior of bit shift operations on these types.

One important aspect here is integer promotion. For fixed width types that are smaller than int, they will first be promoted to int (not int16_t or int32_t) before being applied to most operands. Then you're dealing with potential undefined behavior.

For example, assuming a 32 bit int, this code exhibits undefined behavior:

uint24_t x = 0xffffff;
uint24_t y = x << 8;

Because in the expression x << 8, the value of x is promoted to int, then the shift causes a bit to be shifted into the sign bit of that value.

Upvotes: 5

Related Questions