70

Could someone explain the rationale, why in a bunch of most popular languages (see note below) comparison operators (==, !=, <, >, <=, >=) have higher priority than bitwise operators (&, |, ^, ~)?

I don't think I've ever encountered a use where this precedence would be natural. It's always stuff like:

  if( (x & MASK) == CORRECT ) ...   // Chosen bits are in correct setting, rest unimportant

  if( (x ^ x_prev) == SET )      // only, and exactly SET bit changed

  if( (x & REQUIRED) < REQUIRED )   // Not all conditions satisfied

The cases where I'd use:

  flags = ( x == 6 | 2 );     // set bit 0 when x is 6, bit 1 always.

are near to nonexistent.

What was the motivation of language designers to decide upon such precedence of operators?


For example, all but SQL at the top 12 languages are like that on Programming Language Popularity list at langpop.com: C, Java, C++, PHP, JavaScript, Python, C#, Perl, SQL, Ruby, Shell, Visual Basic.

15
  • 17
    mistake in the original design back in C Apr 11, 2013 at 8:43
  • 7
    @gnat, what is the point of that complaint? The OP didn't say "all", just "a bunch of the most popular languages". And the vast majority follow this order. In this table, only one of the top 12 (SQL) doesn't: langpop.com
    – user82096
    Apr 11, 2013 at 8:59
  • 3
    @dan1111 point is, naturally, to help answerers better understand the question asked and provide better answers. You see, this is not a place for The Guessing Game - or, as tour page says, "It's not a discussion forum. There's no chit-chat."
    – gnat
    Apr 11, 2013 at 9:06
  • 7
    @gnat, I agree with your concern about guessing games, but I don't think this qualifies when nearly every popular language exhibits the described behavior.
    – user82096
    Apr 11, 2013 at 11:18
  • 6
    @Dunk: The common "by hunch" approach is [arithmetics] [logic operator] [arithmetics]. Most programmers don't create a mess of parentheses like if(((x+getLowX()) < getMinX) || ((x-getHighX())>getMaxX()))) - most will assume precedence of arithmetics over logics and write if( ( x + getLowX() < getMinX ) || ( x - getHighX() > getMaxX() )) assuming precedence of + above <. Now intuitively if( x ^ getMask() != PATTERN ) should behave the same, XOR being arithmetic operator. The fact it's interpreted as if( x ^ ( getMask() != PATTERN ) ) is completely counter-intuitive.
    – SF.
    Apr 12, 2013 at 8:03

2 Answers 2

76

Languages have copied that from C, and for C, Dennis Ritchie explains that initially, in B (and perhaps early C), there was only one form & which depending on the context did a bitwise and or a logical one. Later, each function got its operator: & for the bitwise one and && for for logical one. Then he continues

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.

17
  • 1
    Wouldn't that fail if c=2 Or did a==b result in ~0 and not 1?
    – SF.
    Apr 11, 2013 at 11:27
  • 1
    @MasonWheeler: There are cases where one needs to use && with Boolean values to force short-circuiting, or & to prevent it. Further, if x && y had been defined as x ? y : 0, and x || y as x ? x : y [but with the latter only evaluating x once], they could have been useful operations on numbers (which--incidentally--could likely have allowed faster faster execution than the way they're actually defined). Having separate operators is IMHO a good thing, even though I don't like the way they're defined with integers.
    – supercat
    Jul 8, 2014 at 18:50
  • 1
    @MasonWheeler: Especially in embedded systems, it's sometimes necessary to use function-like macros [in some cases, they can offer orders-of-magnitude better performance than in-line functions, and in some embedded systems that can be critical]. Variable declaration within such macros is not possible; using a global variable within a macro might work, but seems really icky.
    – supercat
    Jul 8, 2014 at 20:12
  • 7
    @MasonWheeler: Can you find a Raspberry Pi or Arduino for $0.50 in quantity 1000?
    – supercat
    Jul 8, 2014 at 20:20
  • 1
    @SF.: How often do you use the short-cut operators in contexts where you need the result to be 0 or 1, but the operands might have other values? The fact that comparison operators always yield 0 or 1 is useful, but the fact that && and || coerce all non-zero values to 1 is far less so.
    – supercat
    Feb 6, 2018 at 21:42
7

Bitwise operators are related to logical operators both conceptually and in appearance, which probably explains why they are near each other in the precedence table. Perhaps one could even argue that it would be confusing for & to be higher than ==, yet have && be lower then ==.

Once a precedence precedent (!) was set, it was probably better for other languages to follow it for consistency's sake.

However, I tend to agree with you that this is not optimal. In actual use, bit operators are more like mathematical operators than logical ones, and it would be better if they were grouped with the mathematical operators in precedence.

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.