Discussion re: Treating Arduino pin states as Boolean

Continuing the discussion from ‘if (!digitalRead(buttonPin))’ v ‘(digitalRead(buttonPin))’ . What is the difference?:

What you wanted:

The '!' operator may be applied to any expression to reverse the result when the expression is evaluated.
for example, "if (! (digitalRead(pin) == HIGH)"

regardless of the return value, the result of any C expression is either "true" or "not true".
The comparison that results in either true or not true is best contained with a set of parenthesis to be clear.

The thing to understand is the journey from the actual return value of digitalRead and the "true" and "not true".
The journey is in fact that the same, the evaluation of some values using C rules occurs in the processor itself.

It's up to your software to interpret in the moment what true means and what to do when find out it is true.

You should write an English-language (or your language) sentence and use those words and comparisons;
you could also "#define NOT !" so your code reads "if (NOT (digitalRead(pin))". tbh, this may or may not be true.


More than you wanted:

At this level, you are talking about the C language. Underlying this is how the C compiler works, and how
the machine language being executed by the processor changes based on decisions your code makes.

Any boolean expression is evaluated for a value of "true" or "false". The actual values should never matter.
Decisions in C language are the result of comparing two values; booleans are just a helpful special case.
At the machine language level,
several instructions get the values being compared into registers,
operations (math mostly) occur as instructions execute, the processor internally keeps track of things
instructions are executed that generate several internal results: zero, negative, error, ?.
jump instructions are "jump to this 32-bit value" or "jump depending on the internal value of the zero flag"
the instruction pointer, or, "pointer to the next instruction to execute in the processor", may be changed.

Pins may be written to or may be read from using the value HIGH or LOW. The actual values should never matter.
Obviously a 1-bit pin within the processor accessible with a special function can only have values of 0 and 1.
At the machine level all values are 32-bit values in registers, so all access and comparisons by machine
language are 32-bit values, which means all C variables and function return values end up as 32-bit values.

Pin values are not boolean, unless you learn the underlying way the C compiler implements booleans.
Once you know the underlying method, any thing that has a value can be forced into evaluation as a boolean.

Under these rules, "(digitalRead(pin) == HIGH)" is a boolean expression.
The '!' operator may be applied to any boolean expression to reverse the result when the expression is done.
for example, "if (! (digitalRead(pin) == HIGH)"

at the most fundamental instruction level of the processor itself, based on an internal flags, the next
instruction will either be the instruction that follows this instruction in the pipeline, or a jump instruction
to an instruction that hopefully is in the pipeline. Otherwise the location has to be accessed to begin fetching
the instructions to continue operating; if that operation is far away, the fetching operation may be very costly.

Once you know the underlying method and the way booleans work at the assembly level, read this part.

When you write C code that is "if (value1 == value2) " that causes the compiler to load and compare.

In the case where you have several operations, and "value1" is the result, and "value2" is the integer 0,
the compiler will know it has the result in value1 but it will still follow your direction to do the compare.
An interesting thing is the compiler has to create a value of 0 for the processor to make the comparison.

a more complex rule for booleans says the form "if (value1)" is a comparison of a value to be zero or not,
and most important, the idea that the value zero has the meaning "false", anything not zero is "true".

When using the "if (value1 == value2) value3=0;" form,
the comparison and the jump (around "value3=0;") based on the result will be emitted by the compiler.

In the case of "if (value1)", the compiler knows that it has performed operations and the result is in
value1. What it also knows is the result flags such as zero, negative, etc. are describing value1.

the comparison does not need to execute, so the compiler leaves that instruction out. Time saved.
Because a comparison to a register with the value zero does not need to execute, no register
needs to be assigned a run-time or compile-time value of zero for the comparison. Time saved.
Note also the number of instructions (16-bit or 32-bit at a time) that were eliminated .

What about saving the time it takes to jump to the next instruction? You must either jump or not jump.

At the C level, an expression followed by code will show how this jump is implemented. First comparison,
then the jump to the "this expression was not true" code (after the {} or to the else, out of for/while) occurs.
The "this expression was true" code directly follows the expression, in C and at the machine level. This is
a convention only, the compiler can do what it wants, but it is processing texts that human interact with
so the assumption that asking a question being true would normally be followed by action because it is true.

The person still awake will ask "what about return if zero" rather than "jump if zero": there is no "return"
instruction, instead the code retrieves the caller's return location from the stack or register and jumps there.

So choosing expressions based on the normal and expected outcome can eliminate a lot of time.

If you have for example a sequence of operations that each need to be checked before proceeding,
and you put it into a if/if/else/else structure, only one jump occurs during the success sequence of actions.

if (gadgetRead (something1))
{
     if (gadgetRead (something2))
    {
        output "something1 something2"
        // must jump over failure cases, but if they are returns from a function, may be able to just fall through
        return
    }
   else
   { 
      output "something2 failure"
     return;
   }
}
else
{
    output "something1 failure"
   return;
}

Avoid having a lot of implied knowledge built into the return value from gadgetRead and how it maps
into the true/false boolean paradigm. Write a C wrapper function or macro that has all the calls and checks for
return errors and knows everything about gadgetRead, then have it simply return true or false as a result.

The reason for this long explanation was to be able to point out to a knowledgeable (now at least) reader
that the IEEE coding standards for proper C software in any system requires the form "if (value1 == value2)".
It probably also requires that the comparison be made for the case that is likely to cause the for/while/function
to exit is the comparison so that is upfront and explicit in the code, I didn't read any further after the forced ==.

Thanks for your time and consideration.

sorry that's completely false.... and we use C++ here..

long empty post with lots of misconceptions... not sure why you posted that...

===> "if (! (digitalRead(pin) == HIGH))"?

or

===> "if (! (digitalRead(pin)) == HIGH)"?

In the C language, the pre-increment operator and the post-increment operator act diffently while adding value.

The pre-increment adds one to the value, and then uses the value.

The post-increment uses the value, and then adds one to the value.

If the new language was an incremented value based on C (which it is) then they should have called it ++C.

C is the value, C++ is the post-incremented value of C. When you are writing C++, you are asking the C++
"compiler" to generate C code for you that takes care of all the details such as the value returned by a digital port
being valid within the context of the web search you are performing.

God forbid you want read an Analog port and check the result.

The incremented value of C is useful at some other time, I guess.

No... that's not what the compiler does. If you don't know, please don't post anything. The web is already full of false information and we don't need that in our forum.

stop trolling please.

my typo. The point is don't make a boolean expression from a non-boolean value that you don't know the value of.
you should treat digitalRead as a beast that can only say HIGH or LOW.

so "(digitalRead(pin) == HIGH)" is the expression that resolves as a boolean, and is the object of the NOT operator.

so "if (! (digitalRead(ping) == HIGH) )

Always helpful to fully parenthesize and add spaces or even indents. Sometimes the questions are too hard
to state to be able to expressed any way but a sequence of comparisons and actions.

1 Like

happy to refute any example with assembly code if you want.

very early version of Stroustrup's work was implemented that way - his C++ compiler was generating C. that's 40 years ago though....

Comeau C/C++ is the only compiler that I know of that was going through C and has gone the way of the dodo...

Modern C++ compiler and the one we use here do not go through a C stage.

why do you write such nonsense?

so people will read my posts to see why you respond with short complaints

to long and obviously well-thought through and based on experience comments.

That's called trolling around here I guess.

an example: (Ignoring the fact that we use a C++ compiler and assuming you have some C code in your project...)

This is the definition of what expression statements are

tell me what's the truth value of the empty expression ;
tell me what's the truth value of the expression f(); when I define f as a void function?

void f(void) {
 ;
}

So yes they'll see your long posts that are full of mistakes.

Hopefully they will understand this is just plain wrong and add you to their ignored list..

I'm sure no one here is going to act on my suggestions or information when they fire up their laser.

my request, heartfelt as a newbie, is that you ignore me. Save us all a lot of time.

wow you have to have some power, able to move the entire sequence out of view so you are protected.

I'm beginning to think the Arduino environment is just to amateur to spend time in.

Thanks for making it clear, my friend.

you sure must have spent some time on this one, the reasoning is impeccable.

you are really suggesting that the C++ team said "we need to write a completely new and different C compiler",

and that C compiler was somehow different in how it interprets fundamental C types and activities.

OK.

I would say you sound like a ChatGPT troll, except that even ChatGPT knows the correct answer to those questions. I am sorry to say your knowledge of C++ compilers appears to be about 40 years out of date.

I guess that explains the "shocked" and "old" part :slight_smile:

I've found, on the forumn, people take an explanation of proper resistor value at face value from an expert,

and discussion of platform specific activities relating to reading of digital pins on the platform is trolling.

what a carefully curated bunch of amateurs.

your mistake is thinking the value people get from the Arduino platform is C++.

In fact, as you can see, for some people it just gets in the way.

No worries, microPython will replace C++.

please reference anything in my posts that discusses anything that has to do with C++.

I'm talking about the ARM processor executing all that magic you inherited into one assembly instruction at a time.

I'm shocked at how simplistic and not to mention antagonist the members of the forum are.

I'm old so I know what that means and I go now to find a platform I can build my products on.

Bon voyage!

1 Like