0

In the sketch I'm working on, I'm storing several 1-bit values representing different boolean states in a single byte, trying to save precious RAM. (example for 5 boolean states: dec10 = b00001010, the five states are 0, 1, 0, 1, 0)

I'm then using an if statement to determine if the relevant bit is TRUE or FALSE:
if ((options>>3) & 1 == 1)

The above will evaluate true when the relevant bit is 1, so if I want to carry out something in that case, great, no problem!

However, if I want my if statement to evaluate TRUE in the event that the relevant bit is 0, I can't work out what code I need.


I thought the problem might be that the other 'non-relevant' bits were interfering, so I tried bit shifting the LSB all the way to MSB position and then shifting back to LSB:
if ((options<<4>>7) & 1 == 0)
but this still isn't doing what I'm trying to achieve (and seems like a lot of work for the processor).

It also feels like I've tried just about every combination of ~&0, !&0, ==0, !=0, ~&1, !&1, ==1, !=1 there is, but I must have missed something...

Can anyone tell me how I get an if statement to evaluate true in the event that the bit is a zero?

2

2 Answers 2

4

The simplest thing is just to mask an individual bit, not do any shifting:

if (options & 0x04) {
    // Do something if true
} else {
    // Do something if false
}

Or, to invert it:

if (!(options & 0x04)) {
    // Do something if false 
} else {
    // Do something if true.
}

The thing with C is that 0 is false, and anything else is true. So if your options variable contains 0b00001010 and you AND it with 0x04 (which is HEX for 0b00001000) you end up with 0b00001000. That is 0x04, which is something other than 0, so it's true.

If options contains 0b00000010 and you AND it with 0x04, you get 0b00000000, which is 0. Since 0 is false the result is false.

You don't care what the actual number is - you only care if it's 0.

3
  • This is exactly what I needed, the exclamation mark goes to the left of the statement being evaluated, thanks @Majenko. You've also taught me a much better way of singling out the bit I'm interested in, perfect!
    – Cammack
    Commented Apr 24, 2018 at 22:51
  • 1
    for readibility add a definition like #define myImportantOption 0x04 ..... then use if (options & myImportantOption) { .......... put all the definitions at the beginning of your program, so that you can easily change them if you use a different arduino that may have status bits in a different location
    – jsotola
    Commented Apr 25, 2018 at 0:40
  • @jsotola - good advice for code readability and portability. Would this make any difference to what the ATMEGA sees, or are differences such as this all compiled identically before upload?
    – Cammack
    Commented Apr 25, 2018 at 17:12
2

There is an alternative to doing bit shifts, and that is to use the bit macro which is defined in Arduino.h (and thus is automatically available).

#define bit(b) (1UL << (b))

Now if you want to see if the second bit is set (which as a mask would be 0x04, being 1 shifted left 2 times) you can do this:

if (options & bit (2))
   {
   // bit number 2 is set
   }
else
   {
   // bit number 2 is clear
   }

This is easier to read than:

if (options & (1 << 2))

or:

if (options & 0x04)

And to invert it:

if (!(options & bit (2)))
   {
   // bit number 2 is clear
   }

Or:

if ((options & bit (2)) == 0)
   {
   // bit number 2 is clear
   }

Also, the various processor bits are given as bit numbers in the Atmega include files (rather than bit masks).

WDTCSR register

So, for example, to set some processor bits you can do this:

WDTCSR |= bit (WDCE) | bit (WDE);

That is easier to read than:

WDTCSR |= (1 << WDCE) |  (1 << WDE);

And much easier than turning them into hex numbers:

WDTCSR |= 0x10 | 0x08;

And in case you are wondering how to clear a bit, you and in the 1s complement:

WDTCSR &= ~bit (WDCE);
6
  • What’s wrong with the _BV() macro that’s already included?
    – Gerben
    Commented Apr 25, 2018 at 14:49
  • @Nick - bit is new to me, makes things very easy to read, thank you
    – Cammack
    Commented Apr 25, 2018 at 17:30
  • 1
    @Gerben Because identifiers starting with an underscore and followed by an uppercase letter are reserved and should not be used. For example Identifiers (C++). Use of two sequential underscore characters ( __ ) at the beginning of an identifier, or a single leading underscore followed by a capital letter, is reserved for C++ implementations in all scopes. You should avoid using one leading underscore followed by a lowercase letter for names with file scope because of possible conflicts with current or future reserved identifiers.
    – Nick Gammon
    Commented Apr 25, 2018 at 21:00
  • @Gerben As I pointed out the bit() macro is also already included. I quoted the file it is in (Arduino.h) which will be present in all attempts to compile with the IDE.
    – Nick Gammon
    Commented Apr 25, 2018 at 21:00
  • 1
    @Gerben - the compiler may never "see" it, but let's assume that one day the compiler-writers decide to use _BV for some internal construct (as they are permitted to do, as it is reserved), and the macro form of _BV in Arduino.h overrides that. The compiler will see the difference and not work correctly.
    – Nick Gammon
    Commented Apr 28, 2018 at 1:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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