The precedence of operators defines the order
in which the operators in an expression are evaluated.
We know this from real life as 2 + 3 * 4 = 2 + (3 * 4) = 14
which is not equal to (2 + 3) * 4 = 20
.
I added a parenthesis to emphasize the order of operations.
Programming languages have rules about operator precedence as well. Each language defines its own operator precedence rules, (e.g.: C, Go, Rust) which is great because each language is unique and follows its own goals. It's also terrible because when you switch between languages you never quite know what the exact rules are.
Let's look at an example to see why operator precedence can be confusing.
x = 1 << 2 * 3 + 4
What is the value of x? By applying the operator precedence rules for each language we can figure it out.
// C
x = 1 << ((2 * 3) + 4) = 1024
// Go
x = ((1 << 2) * 3) + 4 = 16
// Rust
x = 1 << ((2 * 3) + 4) = 1024
I added a parenthesis to emphasize the order of operations.
All languages agree that multiplication should come before addition. But Rust and C give bit-shifts lower precedence than addition, while Go treats it the same as multiplication and evaluates from left to right.
I find this confusing.
Sure, it is probably intuitive
what the order of operations in a simple expression 2 * 3 + 4
is.
But that is only because we have PEMDAS
or a variation of it ingrained in our brains.
Keeping in mind rules about operator precedence makes a programming language more complex,
for readers, writers
and for those writing the parser for the programming language itself.
Why not get rid of the cases where operator precedence introduces ambiguity?
A compiler could reject 2 * 3 + 4
because it might not be clear to everybody
what the order of operations would be.
Only expressions like (2 * 3) + 4
would be accepted.
This is more verbose, but it reduces any ambiguity.
A language doing this would save itself the trouble
of defining and implementing an operator precedence table.
It would be simpler to implement and understand.