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 Answers 2
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.
-
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– Cammack2018年04月24日 22:51:11 +00:00Commented Apr 24, 2018 at 22:51
-
1for readibility add a definition like
#define myImportantOption 0x04
..... then useif (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 locationjsotola– jsotola2018年04月25日 00:40:40 +00:00Commented 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– Cammack2018年04月25日 17:12:36 +00:00Commented Apr 25, 2018 at 17:12
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).
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);
-
What’s wrong with the
_BV()
macro that’s already included?Gerben– Gerben2018年04月25日 14:49:02 +00:00Commented Apr 25, 2018 at 14:49 -
@Nick -
bit
is new to me, makes things very easy to read, thank youCammack– Cammack2018年04月25日 17:30:16 +00:00Commented 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.2018年04月25日 21:00:13 +00:00Commented 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.2018年04月25日 21:00:49 +00:00Commented 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.2018年04月28日 01:32:51 +00:00Commented Apr 28, 2018 at 1:32
if ((options>>3) & 1 == 0)
orif ((options>>3) & 1 != 1)
- if neither of them work, you did something wrong