58

In C and C++, the expression some_num & 0xABCD == 5 will effectively evaluate as some_num & (0xABCD == 5). This is unlike all of the standard arithmetic operators, as they have higher precedence than the comparison operators, so some_num + 5 == 5 will evaluate as we expect.

Why is this? It seems very counterintuitive that the standard arithmetic operators have a much higher precedence than the bitwise arithmetic operators.

11
  • 31
    Counterintuitive is such a personal opinion, and it is shrouded in history. Ever since I had a buggy compiler (waayy back in '96), or at least a compiler with unclear precedence rules. I use ( and ) a lot and I find it makes expressions a lot more readable (shows intent). Commented Jul 1 at 17:45
  • 2
    Re-opened this and closed the older as dupe. Regarding double tagging C and C++ (rather than just C) I think it is also appropriate in this case, because the reason why C++ has this precedence order is because it got it from C. Commented Jul 3 at 14:24
  • 2
    @Lundin "... closed the older as a dupe." Just to say., ain't it funny how things work... Commented Jul 3 at 23:55
  • 1
    @Fe2O3 Regarding that, please check this post on meta Commented Jul 4 at 10:55

1 Answer 1

94

This is a historical artifact of the language.

Early versions of C didn't have the || and && operators, so the | and & operators were used for logical AND and logical OR in cases where the operands had values of either 0 or 1.

As a result, these operators were given lower precedence to fit into this use case.

When the || and && operators were later added around 1972, the precedence of | and & remained lower than that of arithmetic and relational operators to avoid breaking existing code, and that decision persists to today.

From Dennis Ritchie's paper, The Development of the C Language:

Rapid changes continued after the language had been named, for example the introduction of the && and || operators. In BCPL and B, the evaluation of expressions depends on context: within if and other conditional statements that compare an expression's value with zero, these languages place a special interpretation on the and (&) and or (|) operators. In ordinary contexts, they operate bitwise, but in the B statement

if (e1 & e2) ...

the compiler must evaluate e1 and if it is non-zero, evaluate e2, and if it too is non-zero, elaborate the statement dependent on the if. The requirement descends recursively on & and | operators within e1 and e2. The short-circuit semantics of the Boolean operators in such `truth-value' context seemed desirable, but the overloading of the operators was difficult to explain and use. At the suggestion of Alan Snyder, I introduced the && and || operators to make the mechanism more explicit.

Their tardy introduction explains an infelicity of C's precedence rules. In B one writes

if (a==b & c) ...

to check whether a equals b and c is non-zero; in such a conditional expression it is better that & have lower precedence than ==. In converting from B to C, one wants to replace & by && in such a statement; to make the conversion less painful, we decided to keep the precedence of the & operator the same relative to ==, and merely split the precedence of && slightly from &. Today, it seems that it would have been preferable to move the relative precedences of & and ==, and thereby simplify a common C idiom: to test a masked value against another value, one must write

if ((a&mask) == b) ...

where the inner parentheses are required but easily forgotten.

Sign up to request clarification or add additional context in comments.

9 Comments

"to avoid breaking existing code" => I've heard it told as saying there were thousands of lines of code already at a time, so it was judged not worth the effort to go and update them...
This right here is why I require parenthesis to isolate non-commutative binary operators from eachother (e.g., a + b == c --> (a + b) == c, a + b * c -> a + (b * c) ) it is probably not necessary, but the amount of times this has made some realize they really meant (a + b) * c instead is depressingly high while a * b *c and a + b - c remain unchanged.
@Questor: Multiplication vs addition is sufficiently well established I wouldn't demand parentheses there, but your main point is one that isn't emphasized enough: language rules may say unambiguously what code will do, but not what the person who wrote it was expecting it to do. The way a Java compiler will process something like double1 = float1*float2; may be clear, but nothing in that code would make clear whether the programmer actually meant double1 = (double)float1 * float2;.
@supercat - clearly you're not a smalltalk programmer, then. :)
@supercat "language rules may say unambiguously what code will do" Your Honour. The prosecution calls inline to the stand, after which we plan to call upon auto to explain itself. (One may claim languages 'run' on an abstract machine, and implementations are expected to have their idiosyncrasies, I suppose...)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.