Skip to content

This site describes solve-engine 2.44.0, the version npm installs.

Bitwise operators

Packages: ARITHMETIC_PACKAGE, FUNCTION_PACKAGE, CONVERTERS_PACKAGE, UOM_PACKAGE, BIGINT_PACKAGE. Registered by createEngine(); for a slimmer engine, register them explicitly (see choosing packages).

A bitwise operator combines two numbers bit by bit rather than as whole values: & keeps a bit only where both have it, | where either does, xor where exactly one does, and ~ flips every bit. These are the building blocks of flags and masks.

& and | are and and or, ~ complements every bit, and exclusive or is the word xor.

0xFF & 0x0F // 15
0xF0 | 0x0F // 255
0xFF xor 0x0F // 240
0b1010 & 0b0110 // 2
0b1010 | 0b0110 // 14
0b1010 xor 0b0110 // 12
~5 // -6
~0 // -1

~ flips all 32 bits, which for a positive number means ~n is -(n+1).

Exclusive or is a word because ^ is already exponentiation, which is the far more common thing to want on a page of sums. 2^10 is a thousand and change, not three.

2^10 // 1,024

Programs written in C or JavaScript often keep a whole number in 32 bits, and three functions give the answers such a program would. clz32(n) counts the leading zero bits of n written in 32 bits, so 1 has 31 and 0 has all 32; 32 minus the count is how many bits the number needs. imul(a, b) multiplies the way 32-bit integers do, wrapping round when the product does not fit, which is what a hash function written in those languages computes. fround(x) rounds a number to the nearest single-precision float, the 32-bit format graphics cards and many file formats store, so it shows what a value becomes once stored that way.

clz32(1) // 31
clz32(0) // 32
imul(3, 4) // 12
imul(2147483647, 2) // -2
fround(5.5) // 5.50

These operators follow the precedence order that C, JavaScript, Python and their relatives share, so an expression that mixes them means what a programmer reads it as. Loosest to tightest: |, then xor, then &, then the comparisons, then the shifts, then + and -, then * and /.

1 | 2 << 3 // 17
1 + 2 << 3 // 24
4 & 3 + 1 // 4
4 | 6 & 3 // 6

Read those as 1 | (2 << 3), (1 + 2) << 3, 4 & (3 + 1) and 4 | (6 & 3). The arithmetic happens first, then the shift, then the bitwise operators, and & wins against |.

Brackets still cost nothing, and on a line that mixes three or four of these they read better than a precedence table does.

(0xF0 | 0x0F) & 0xFF // 255
(1 << 8) | 1 // 257

and is not a bitwise operator. It is the plain English word, and it adds.

5 and 3 // 8