Operator Precedence: How Programming Languages Evaluate Expressions

What does this expression mean?

2 + 3 * 4

Is the answer 20 or 14?

Most programming languages evaluate multiplication before addition, so the answer is 14. This rule is called operator precedence. It tells the language how to group parts of an expression when parentheses are absent.

Precedence is useful, but it can make long expressions hard to read. In this post, we’ll cover common operators, how expressions are grouped, where languages differ, and how to write code whose meaning is clear.

A useful distinction: grouping versus evaluation

People often say that operators “run” in a particular order. There are two ideas hidden in that phrase:

  1. Precedence and associativity determine how an expression is grouped.
  2. Evaluation order determines when the parts of that expression are evaluated.

For example, multiplication has higher precedence than addition. The expression 2 + 3 * 4 is grouped as 2 + (3 * 4).

But the rules for evaluating function arguments or the left and right sides of an operator are separate. They can differ between languages, and compilers may optimize the generated instructions. Precedence describes the meaning of an expression, not a universal sequence of CPU instructions.

Common operator groups

These are common categories, from higher to lower precedence. This is a general guide, not a universal table: each language defines its own rules.

CategoryCommon operatorsExample
Grouping and access(), ., [], function callsuser.name, items[0]
Unary!, not, -x, ~x!ready, -count
Exponentiation**, sometimes ^ in math-oriented languages2 ** 3
Multiplication and division*, /, %amount / count
Addition and subtraction+, -total - discount
Shifts<<, >>value >> 1
Relational comparisons<, <=, >, >=age >= 18
Equality comparisons==, !=, ===name == "Ada"
Bitwise&, ^, ``
Logical AND&&, andis_ready && has_data
Logical OR`
Conditional expressioncondition ? a : bscore >= 60 ? "pass" : "fail"
Assignment=, +=, -=, *=, /=total += price

Check the language you’re using when exact grouping matters.

Arithmetic precedence

The familiar arithmetic rules apply in many languages:

  1. Parentheses
  2. Exponents
  3. Multiplication, division, and remainder
  4. Addition and subtraction

For example:

result = 10 + 6 / 2 * 3
print(result)  # 19.0

Multiplication and division have equal precedence, so they are grouped from left to right:

10 + ((6 / 2) * 3)
10 + (3 * 3)
19

Addition and subtraction also have equal precedence and are grouped from left to right:

print(20 - 5 + 2)  # 17

That means (20 - 5) + 2, not 20 - (5 + 2).

Division and remainder depend on the language

The / operator does not behave exactly the same in every language. In Python, / produces a floating-point result:

print(7 / 2)   # 3.5
print(7 // 2)  # 3 (floor division)

In JavaScript, / also produces a number, including for integer inputs:

console.log(7 / 2); // 3.5

In C and C++, dividing two integers performs integer division:

#include <iostream>

int main() {
    std::cout << 7 / 2 << '\\n'; // 3
}

For positive values, remainder is intuitive:

print(7 % 2)  # 1

With negative values, remainder and modulo conventions can differ between languages. If negative operands are possible, check the language documentation.

Parentheses make grouping visible

Parentheses can override the usual precedence:

print((2 + 3) * 4)  # 20
print(2 + 3 * 4)    # 14

They also make complicated expressions easier to understand:

final_price = (price * (1 - discount_rate)) + shipping

The parentheses around 1 - discount_rate are not required for precedence here, but they make the intended calculation easier to see.

Comparisons and logical operators

A common pattern is to compare values, then combine the results:

age = 24
has_ticket = True

can_enter = age >= 18 and has_ticket
print(can_enter)  # True

In Python, comparisons have higher precedence than and, so this groups as (age >= 18) and has_ticket. JavaScript uses a similar relationship between comparisons and logical AND or OR:

const canEnter = age >= 18 && hasTicket;

When a condition has several parts, parentheses make the intended logic clear:

const canEnter = (age >= 18 && hasTicket) || hasStaffPass;

This means the person can enter if they are an adult with a ticket, or if they have a staff pass.

AND usually binds more tightly than OR

In Python and JavaScript, logical AND has higher precedence than logical OR. This JavaScript expression:

const result = a || b && c;

is grouped as a || (b && c), not (a || b) && c. Parentheses make it clearer:

const result = a || (b && c);

Short-circuit evaluation

Logical operators often stop as soon as the result is known. This is called short-circuit evaluation.

In JavaScript:

const user = null;

if (user && user.isAdmin) {
  console.log("Admin");
}

Because user is null, JavaScript does not evaluate user.isAdmin, avoiding an error.

The same idea works with OR:

const displayName = userProvidedName || "Guest";

If userProvidedName is truthy, the expression stops there. Otherwise it uses "Guest". Short-circuit behavior is common, but operators and truthiness rules vary by language. Python uses and and or; JavaScript commonly uses && and ||.

Associativity: what happens when precedence is tied?

Precedence answers which operator groups first when operators differ. Associativity answers how operators at the same precedence group.

Most arithmetic operators such as +, -, *, and / associate left to right. So 24 / 4 / 2 is grouped as (24 / 4) / 2, which gives 3, not 12.

Assignment operators usually associate right to left in languages such as JavaScript and C:

let a, b, c;
a = b = c = 5;

This groups from the right: a = (b = (c = 5)).

Floating-point arithmetic can surprise you

Computers represent many decimal fractions approximately. Regrouping math operations can therefore change the result:

console.log((0.1 + 0.2) + 0.3);
console.log(0.1 + (0.2 + 0.3));

The printed values can differ in their last few decimal digits. For exact decimal arithmetic, such as money calculations, use an appropriate decimal type or library for your language.

Important differences between languages

Some operators look familiar but have different meanings or precedence rules across languages.

^ does not always mean exponentiation

In Python, ** means exponentiation:

print(2 ** 3)  # 8

But ^ means bitwise XOR:

print(5 ^ 3)  # 6

In many languages, including JavaScript and C, ^ is also bitwise XOR. In languages such as Excel formulas and some mathematical tools, ^ is used for exponentiation. So this Python code does not calculate two to the third power:

print(2 ^ 3)  # 1, because this is bitwise XOR

Exponentiation and unary minus can differ

Python treats exponentiation as having higher precedence than unary minus on the left:

print(-2 ** 2)    # -4
print((-2) ** 2)  # 4

The first expression means -(2 ** 2). In JavaScript, -2 ** 2 is a syntax error. Use parentheses to make the grouping clear:

console.log(-(2 ** 2));   // -4
console.log((-2) ** 2);   // 4

Equality is not the same in every language

JavaScript has both loose equality (==) and strict equality (===):

console.log(5 == "5");  // true
console.log(5 === "5"); // false

Python uses == for value equality and has no === operator:

print(5 == "5")  # False

The operator spelling alone does not guarantee the same behavior across languages.

Chained comparisons are language-specific

Python allows mathematical-looking comparison chains:

age = 24
print(18 <= age < 65)  # True

This checks both comparisons. Do not assume the same notation works the same way everywhere. In JavaScript, write comparisons explicitly:

const workingAge = age >= 18 && age < 65;

A practical way to read a long expression

Take this JavaScript expression:

const allowed =
  isLoggedIn && (isAdmin || (hasInvite && accountIsActive));

Read the parentheses from the inside out:

  1. hasInvite && accountIsActive
  2. isAdmin || the result of step 1
  3. isLoggedIn && the result of step 2

The parentheses make the rule explicit: the user must be logged in, and they must be an admin or have an invite while their account is active.

If an expression takes several rereads to understand, split it into named steps:

const hasValidInvite = hasInvite && accountIsActive;
const hasPermission = isAdmin || hasValidInvite;
const allowed = isLoggedIn && hasPermission;

The extra lines often make the code easier to change and review.

A small precedence puzzle

What does this print?

console.log(2 + 3 * 4 > 10 && !false);

Apply the operators in groups:

(2 + (3 * 4) > 10) && (!false)

Then evaluate:

(2 + 12 > 10) && true
14 > 10 && true
true && true
true

The result is true. Try rewriting the expression with parentheses to make each step visible:

console.log(((2 + (3 * 4)) > 10) && (!false));

A fun bitwise trick: toggle a flag

Bitwise XOR (^) can flip a single bit. Suppose the lowest bit represents whether a light is on:

light_is_on = 0
light_is_on ^= 1
print(light_is_on)  # 1

light_is_on ^= 1
print(light_is_on)  # 0

The ^= operator combines XOR with assignment. Each time it runs, it toggles the bit between 0 and 1. This also shows how the same symbol can mean different things: in Python, ^ is a bitwise operator, not exponentiation.

Does the CPU follow the precedence table?

Not directly. A programming language defines what an expression means. A compiler or interpreter translates that meaning into operations the computer can perform.

A compiler may simplify or rearrange work when language rules allow it. Modern CPUs can also execute some independent instructions in parallel. The exact machine instructions depend on the language, compiler, processor, and surrounding code.

For everyday programming, focus on the language’s rules for precedence, associativity, and evaluation order. You usually do not need to predict the exact order of CPU instructions.

Good habits for writing expressions

  • Use parentheses when the intended grouping might not be obvious.
  • Break long conditions into named variables.
  • Check your language’s precedence rules when using unfamiliar operators.
  • Be especially careful with ^, /, equality operators, and negative numbers.
  • Do not rely on side effects inside a complicated expression. Put them on their own line.
  • Use a calculator or a small code example to check tricky cases.

Final thought

Operator precedence helps a language interpret an expression, but clear code helps people interpret it. Parentheses and small named steps are inexpensive ways to show what you mean—and they make bugs easier to spot.

When in doubt, make the grouping explicit.