Stephen Wicklund
Stephen Wicklund

Reputation: 71

Does the position of this bit-wise operator change the behavior?

Are these two lines of code equivalent?

P1->OUT &= ~(uint8_t)(1<<1);

P1->OUT &= (uint8_t)(~(1<<1));

Upvotes: 4

Views: 102

Answers (2)

Jonathan Leffler
Jonathan Leffler

Reputation: 753585

TL;DR — No! (They are not always the same, and the result depends on the type of P1->OUT.)

I'm assuming the normal case of 2's-complement arithmetic. If you suffer from sign-magnitude or 1's-complement arithmetic, you have to think quite a lot harder, but the conclusion is probably the same (not always the same).

Case 1:

  • (1<<1) is an int value (0x02).
  • (uint8_t)(1<<1) converts to uint8_t, but the value is still 0x02.
  • ~(uint8_t)(1<<1) converts the uint8_t value to int again (usual arithmetic conversions) and applies the bit-wise inversion operator ~ to the result.

Assuming a 32-bit int type, you get 0xFFFFFFFD to & with P1->OUT:

P1->OUT &= 0xFFFFFFFD;

Case 2:

  • (1<<1) is an int value (0x02).
  • ~(1<<1) is an int value — assuming 32-bit int, the value is 0xFFFFFFFD.
  • (uint8_t)~(1<<1) casts to uint8_t, zeroing the bits in the more significant bytes.

Assuming a 32-bit int type, you get 0x000000FD to & with P1->OUT:

P1->OUT &= 0x000000FD;

So, the value combined with P1->OUT is different, but the effect depends also on the (unspecified) type of P1->OUT. If it is uint8_t, then there's no difference; if it is uint64_t, there is potentially a massive difference, but it also depends on the value in P1->OUT before the compound assignment is executed.

Upvotes: 3

It depends on the type of P1->OUT, and the system. The result of 1 << 1 is of type int.

I am considering the general case of int n; instead of 1 << 1

In

P1->OUT &= ~(uint8_t)(n);

the operand will be widened to int again before the ~ (the integer promotion), and the ~ would be applied to an int. The result will have all the high-order bits 8...k set. If P1->OUT is 8 bits wide it is OK, but if it has more bits then the result is not what you'd expect.

This one is even worse:

P1->OUT &= (uint8_t)(~(n));

The ~ operand will be applied again to an int and that would be converted to uint8_t. now if ~n is actually negative (has its sign bit set) - and in case of ~(1 << 1) it would be negative - it will be fine in two's-complement implementations but totally incorrect in 1's-complement and sign-and-magnitude implementations, because the bit representations would not be the same.


The proper way to do bit twiddling is always use unsigned int or a wider two's complement number:

P1->OUT &= ~(1U << n);

or

P1->OUT &= (~(1U << n)) & 0xFF;

to avoid the arithmetic conversions and integer promotions ever producing signed numbers.

Upvotes: 3

Related Questions