rbowman <
bowman@montana.com> wrote or quoted:
On Thu, 3 Oct 2024 03:04:11 -0600, Louis Krupp wrote:
Maybe add parentheses around the comparison, especially if it's long or
complex, which this isn't:
I tend to use parens with operators if it isn't a simple expression to
make the precedence explicit.
Could you play this sonata for us?
Pianist plays the Beethoven Sonata from memory.
(He also knows some other pieces by heart.)
Could you translate this sentence for us?
Translator translates the sentence, after having learned
more than 10000 words, many of which have several forms
and meanings, idioms, and complex rules of grammar and
usage.
Could you explain "a & b << c + 1" for us?
Look Sir, I'm a professional C programmer, doing this eight
hours a day, 40 hours a week, for many years. But C has
about 17 levels of precedence! Memorizing all of them is
simply beyond human capabilities! So please give me some parens.
I would say, otherwise, you would be to blame for having
written such a cluttered mess!
~~~
C is a language with some subtleties, and programming often
is somewhat complex overall. If you don't trust a C programmer
to even get an expression as simple as "a & b << c + 1",
how is he supposed to get more subtle things right?
~~~
We start with -2 * 3 + 4.
We do not have to memorize precedence here, because we know
it from school. So, we have
prefix - ! * ...
multiplicative * /
additive + -
. To get the full list of the "calculations", as I call
these operations, we just need to add one layer "postfix"
above and one layer "shift" below:
postfix [] () ...
prefix - ! * ...
multiplicative * /
additive + -
shift << >>
. One can remember the position of those layers by thinking
about the meaning one wants to have to expressions like -a[ 3 ]
and 1 << 2 + 3. The amount of a shift is often expressed
with arithmetic expressions, so we want no parens there.
Now we'll go beyond what I called "calculations":
The results of calculations usually are compared. We rarely
want ( 2 < 3 )+ 1, but more often 2 <( 3 + 1 ).
postfix [] () ...
prefix - ! * ...
multiplicative * /
additive + -
shift << >>
comparisons < > <= >=
Though both are quite rare, (x>0)==(y>0) still should be
needed more often than (x==0)>(y==0).
postfix [] () ...
prefix - ! * ...
multiplicative * /
additive + -
shift << >>
comparisons < > <= >=
equalities == !=
When shifting, in the end, we often mask using "&". So we
want to be able to write "word & 1 << 3" without parens.
postfix [] () ...
prefix - ! * ...
multiplicative * /
additive + -
shift << >>
comparisons < > <= >=
equalities == !=
bit and &
Sometimes, one might want to write x==0&y==0 without
shortcut evaluation (to avoid a jump-operation), so "&"
has less precedence than "==".
The "and" operation is very similar to multiplication:
0*0=0, 0*1=0, 1*0=0, 1*1=1. The "or" operation is very
similar to addition: 0+0=0, 0+1=1, 1+0=1, 1+1=2.
So, "and" is "multiplicative" and "or" is "additive".
Additive operations have a smaller precedene than
multiplicative operations.
postfix [] () ...
prefix - ! * ...
multiplicative * /
additive + -
shift << >>
comparisons < > <= >=
equalities == !=
bit and &
bit or |
The bitwise exclusive or is neither clearly additive nor
clearly multiplicative, so it's middle ground, so to speak.
postfix [] () ...
prefix - ! * ...
multiplicative * /
additive + -
shift << >>
comparisons < > <= >=
equalities == !=
bit and &
bit exclusive or ^
bit or |
The logical operations are "high level" operations, since
one might want to require to bits to be set via x&3 && y&4.
postfix [] () ...
prefix - ! * ...
multiplicative * /
additive + -
shift << >>
comparisons < > <= >=
equalities == !=
bit and &
bit exclusive or ^
bit or |
logical and &&
logical or ||
The "if" expression is as high level as a statement. One wants
to be able to write: x > 2 && y > 3 ? ... .
postfix [] () ...
prefix - ! * ...
multiplicative * /
additive + -
shift << >>
comparisons < > <= >=
equalities == !=
bit and &
bit exclusive or ^
bit or |
logical and &&
logical or ||
if ?:
(Possibly not all details of the grammar of the ternary
operator can be specified using the concept of precedence,
and so one might need to refer to the actual grammar.)
And whatever expression you write, you want to be able to
write it on the right-hand side of an assignment operator
without parantheses.
postfix [] () ...
prefix - ! * ...
multiplicative * /
additive + -
shift << >>
comparisons < > <= >=
equalities == !=
bit and &
bit exclusive or ^
bit or |
logical and &&
logical or ||
if ?:
assignment =
One exception to this is when you want to write a
sequence of assignments, such as: a=2,b=3.
postfix [] () ...
prefix - ! * ...
multiplicative * /
additive + -
shift << >>
comparisons < > <= >=
equalities == !=
bit and &
bit exclusive or ^
bit or |
logical and &&
logical or ||
if ?:
assignment =
comma ,
It might help to add some structure:
calculations postfix [] () ...
prefix - ! * ...
multiplicative * /
additive + -
shift << >>
comp./eq. comparisons < > <= >=
equalities == !=
bit related bit and &
bit exclusive or ^
bit or |
logical logical and &&
logical or ||
if ?:
assignment assignment =
comma comma ,
So one could start out learning the coarse first column
with only six levels by heart, and the "details" from the
seconds column later.
One also can observe that the operators from [] to | are
used to "obtain" values, while the operators from ?: to
, are used to "process" values, and "&&" and "||" are in
between.
(I wrote all of the above from memory, but looked at a
precedence table yesterday.)
No way, José! Those were actually all snippets from my old posts
about wrapping your head around operator precedence. Talk about
a blast from the past! I've been beating that drum for ages.
It's like trying to explain the ins and outs of In-N-Out's secret
menu - takes practice, but once you get it, you're golden.
Just got to keep plugging away until it clicks, you know?