Boolean logic in C: logical vs bitwise operators
C has logical operators that produce 0 or 1 and bitwise operators that work on every bit, with similar spellings and different precedence. Any non-zero value is true, which makes if (x = 1) and a & b == c the two classic slips.
| Logic | C | Note |
|---|---|---|
NOT A | !a | ~a flips every bit |
A AND B | a && b | a & b is bitwise |
A OR B | a || b | a | b is bitwise |
A XOR B | !a != !b | a ^ b is bitwise; normalise first |
A -> B | !a || b | No implication operator |
A <-> B | !a == !b | Normalise before comparing |
TRUE, FALSE | non-zero, 0 | true/false with <stdbool.h> or C23 |
!a, a && b and a || b are the logical operators; they treat any non-zero operand as true and produce exactly 1 or 0. ~a, a & b, a | b and a ^ b are bitwise: 5 && 2 is 1, but 5 & 2 is 0, because the integers share no set bit. Since C99 there is _Bool (spelled bool with the header, and a keyword in C23), and converting to it normalises any non-zero value to 1; !!x does the same in older code.
Precedence is where C bites. ! binds tightest; then the arithmetic and shift operators; then the comparisons == and !=; then &, ^, | in that order; then &&; then ||. So a & b == c is a & (b == c), which is almost never what was meant, and a == 1 && b == 2 is fine only because == outranks &&. Assignment ranks lowest, so if (x = 1) assigns and tests the value 1, which is always true.
Tangibool does not export C. Translate with the table, bracket every mixed expression, and keep the engine's truth table as the test oracle. The parser rejects &, |, ^ and ~ on input precisely because a C expression that uses them would otherwise change meaning when read as logic; with &&, || and ! a C condition is usually a valid formula as it stands.
The common slip
Comparing flags with == without normalising. If one function returns 1 for true and another returns 4 (a set bit), both are true in C, but flag1 == flag2 is false. Compare !!flag1 == !!flag2, or convert to bool at the boundary.
Questions
Is a ^ b usable as logical XOR?
Only when both operands are already 0 or 1. On arbitrary integers it is bitwise, so 2 ^ 4 is 6, which is true, although both inputs were true and a logical XOR should give false. Use !a != !b.
Why does the engine reject ~?
Because ~ is bitwise complement in C and its relatives, and ~1 is -2, which is true, not false. The parser offers NOT as the repair rather than guessing which meaning you wanted.
Related
See also
- Boolean logic in Java: &&, ||, ! vs &, | and ^
- Boolean logic in JavaScript: &&, ||, !, truthiness and precedence
- Single & and | are bitwise in most languages
- ~ is bitwise NOT in code, logical NOT in some textbooks
- ^ means XOR in C, Java and Python, not AND or power
- Precedence differs across languages: not a == b in Python
- Operator precedence in Boolean logic: NOT, then AND, then XOR, then OR
Languages
Generated from the Tangibool engine, semantics tangibool-semantics/1, engine 1.0.0, build . Every table, map, simplification and verdict on these pages was computed by the Tangibool engine when the site was built, and every claim was re-checked by the same exact checker the app uses. Limits are stated where they apply. © 2026 Foreranger LTD.