Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparing a bit to a boolean

Say I have a set of flags, encoded in a uint16_t flags. For example, AMAZING_FLAG = 0x02. Now, I have a function. This function needs to check if I want to change the flag, because if I want to do that, I need to write to flash. And that is expensive. Therefore, I want a check which tells me if flags & AMAZING_FLAG is equal to doSet. This is the first idea:

setAmazingFlag(bool doSet)
{
    if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0)) {
        // Really expensive thing
        // Update flags
    }
}

This is not an intuitive if statement. I feel like there should be a better way, something like:

if ((flags & AMAZING_FLAG) != doSet){

}

But this does not actually work, true seems to be equal to 0x01.

So, is there a neat way to compare a bit to a boolean?

like image 742
Cheiron Avatar asked Feb 20 '20 10:02

Cheiron


People also ask

Is boolean true 1 or 0?

Boolean values and operations Constant true is 1 and constant false is 0. It is considered good practice, though, to write true and false in your program for boolean values rather than 1 and 0.

Does Bitwise and work on Booleans?

You ask: “Is there any reason not to use the bitwise operators & , | , and ^ for "bool" values in C++? ” Yes, the logical operators, that is the built-in high level boolean operators ! , && and || , offer the following advantages: Guaranteed conversion of arguments to bool , i.e. to 0 and 1 ordinal value.

How many values a boolean variable can have?

A variable of the primitive data type boolean can have two values: true and false (Boolean literals).


4 Answers

To convert any non-zero number to 1 (true), there is an old trick: apply the ! (not) operator twice.

if (!!(flags & AMAZING_FLAG) != doSet){
like image 103
user253751 Avatar answered Oct 16 '22 19:10

user253751


You need to convert the bit mask to a boolean statement, which in C is equivalent to values 0 or 1.

  • (flags & AMAZING_FLAG) != 0. The most common way.

  • !!(flags & AMAZING_FLAG). Somewhat common, also OK to use, but a bit cryptic.

  • (bool)(flags & AMAZING_FLAG). Modern C way from C99 and beyond only.

Take any of the above alternatives, then compare it with your boolean using != or ==.

like image 2
Lundin Avatar answered Oct 16 '22 17:10

Lundin


From a logical point of view, flags & AMAZING_FLAG is only a bit operation masking all other flags. The result is a numerical value.

To receive to a boolean value, you would use a comparison

(flags & AMAZING_FLAG) == AMAZING_FLAG

and can now compare this logical value to doSet.

if (((flags & AMAZING_FLAG) == AMAZING_FLAG) != doSet)

In C there may be abbreviations, because of the implicit conversion rules of numbers to boolean values. So you could also write

if (!(flags & AMAZING_FLAG) == doSet)

to write that more terse. But the former version is better in terms of readability.

like image 1
Ctx Avatar answered Oct 16 '22 19:10

Ctx


You can create a mask based on doSet value:

#define AMAZING_FLAG_IDX 1
#define AMAZING_FLAG (1u << AMAZING_FLAG_IDX)
...

uint16_t set_mask = doSet << AMAZING_FLAG_IDX;

Now your check can look like this:

setAmazingFlag(bool doSet)
{
    const uint16_t set_mask = doSet << AMAZING_FLAG_IDX;

    if (flags & set_mask) {
        // Really expensive thing
        // Update flags
    }
}

On some architectures, !! may be compiled to a branch and by this, you may have two branches:

  1. Normalisation by !!(expr)
  2. Compare to doSet

The advantage of my proposal is a guaranteed single branch.

Note: make sure you don't introduce undefined behaviour by shifting left by more than 30 (assuming integer is 32 bits). This can be easily achieved by a static_assert(AMAZING_FLAG_IDX < sizeof(int)*CHAR_BIT-1, "Invalid AMAZING_FLAG_IDX");

like image 1
Alex Lop. Avatar answered Oct 16 '22 17:10

Alex Lop.