0
\$\begingroup\$

I'm coding for microcontrollers and I've come across two styles of bitwise operations that quite confuse me a lot because they are used interchangeably. First let's assume our REGISTER has 4 bits; bit 0,1,2,3. The default value of REGISTER on reset is 1100.

1.what the difference between:

REGESTER ^= (1<<3) | (1<<2);

&

REGESTER &= ~( (1<<3) | (1<<2) );

2.Is the first one correct? ( I'm not certain that it's correct )

3.If the first one's correct, then why use the second statement as the resulting value is same as the second one?

Final Result: 0000

\$\endgroup\$
  • \$\begingroup\$ google DeMorgan’s theorem \$\endgroup\$ – jsotola Mar 30 '18 at 18:33
2
\$\begingroup\$

These are not interchangeable in general. You get the same results in this particular case, because the first one toggles bits 3 and 2, the second one clears them. Which one is correct depends on what you want to accomplish

\$\endgroup\$
  • \$\begingroup\$ Well, I'm writing a HAL for STM32 and from code examples I've seen, they use &= ~x to set the GPIO Register MODE. So, it kind of confused me, why not use ^x instead of &= ~x \$\endgroup\$ – Killerbee89 Mar 30 '18 at 18:46
  • 1
    \$\begingroup\$ @Killerbee89 Because they cannot assume that the registers will be in their reset state. \$\endgroup\$ – duskwuff Mar 30 '18 at 18:57
5
\$\begingroup\$
  1. ^= x is XOR. It flips the state of bits.

    &= ~x is AND NOT. It clears bits.

  2. ^= x is only correct if toggling the bits is what you want to do every time this code runs. This might be correct if you wanted to flash an LED, for example, but it shouldn't be used if you want to ensure that the register ends up in a specific state.

\$\endgroup\$
  • \$\begingroup\$ and REGESTER |= (1<<2) set bit 2 \$\endgroup\$ – G36 Mar 30 '18 at 18:41
2
\$\begingroup\$

In addition to the already posted answers and understanding what these operators do, it is equally important to understand how dangerous bitwise operators are.

You are using integer literals like 1, a literal which is of type (signed) int. Signed operators are dangerous to use together with bitwise operators.

Many operators like |, ^ and ~ don't care about the type of the operands, they work on the raw binary level. But the shift operators can invoke various nasty forms of undefined behavior bugs, should you shift a signed operand. For example, the code 1 << 31 invokes undefined behavior, since it shifts data into the sign bits. Similarly, right shifts of signed variables have implementation-defined behavior if the operand is negative.

To make things worse, there are implicit integer promotions. Code such as this also invokes undefined behavior:

unsigned char byte = 1;
byte = ~byte << 1;

Because it is the same thing as 0xFFFF FFFE << 1. Anything can happen here, including a program crash.

Similarly, code such as this will behave unexpectedly:

uint8_t mask = 0xF0;
uint8_t data = 0x01;
if(data > ~mask)
{
  puts("0x01 is larger than 0x0F");
}

What this means in practice is that things like the mentioned De Morgan's laws do not necessarily apply transparently to C code! Implicit type promotions and poorly-specified behavior can cause havoc.

The solution is to avoid signed types in embedded systems, and to write code as free from implicit promotions as possible.

More info: Implicit type promotion rules.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.