Summary#
Osy# has the whole C# bitwise family — & (and), | (or), ^ (exclusive or), ~ (complement), << (left shift)
and >> (right shift) — together with the compound forms &=, |=, ^=, <<= and >>=.
They work on int, and they mean exactly what they mean in C#. The two differences worth knowing are both about
what they do not do: they never raise on overflow, and they do not accept a bool.
Signature#
int r = (colour >> 16) & 255; // read one byte out of a packed value
int packed = (r << 16) | (g << 8) | b; // put three back together
int flags = Read | Write; // set bits
bool canWrite = (flags & Write) != 0; // test one
flags &= ~Write; // clear it
int doubled = value << 1; // shift
int halved = value >> 1; // arithmetic — the sign is preservedDescription#
They are unchecked#
Osy# integer arithmetic is checked: +, -, * and / raise rather than wrapping to a wrong number when the
result leaves the range of int. Bitwise operators are the deliberate exception, because a bit operation is defined
modulo 2^32 rather than as arithmetic on a magnitude.
So 1 << 31 is -2147483648, and that is the answer rather than an error — the same as in C#. If it raised, a
perfectly ordinary bit pattern could not be written down.
The shift count wraps at 32#
x << 33 means x << 1: the count is masked to its low five bits, as in C#. A shift by a multiple of 32 is
therefore a shift by nothing, not a way to clear a value.
>> keeps the sign#
Right shift is arithmetic: -8 >> 1 is -4, not a large positive number. The sign bit is copied rather than
zeros being shifted in. This is why the family is defined on int and not on a wider or unsigned type — there is
exactly one integer width whose bit behaviour is identical everywhere an Osy# expression can run.
Precedence is C#'s#
From loosest to tightest:
|| < && < | < ^ < & < == != < < <= > >= < << >> < + - < * / %Two consequences catch people out in C too, and they are the same here:
a & b == cisa & (b == c)— equality binds tighter than&.1 << 2 + 1is1 << 3, which is8— addition binds tighter than a shift.
Parenthesise when the reading matters. The compiler will not warn, because the expression is not wrong.
The operands must be integers#
A double has no bit pattern the language exposes, so x & 255 on a double is refused rather than rounded — the
same refusal C# makes.
A bool is also refused, and here Osy# is narrower than C#. C# lets you write a & b on two bools as a
non-short-circuiting logical and: both sides are evaluated, and the result is a bool. That is a genuinely different
operation from the integer one, and it is not implemented. It is refused by name rather than quietly treated as
&&, which would be the same expression meaning two different things depending on a type you cannot see at the call
site. Use && and ||.
Where they run#
Everywhere. A bitwise expression compiles for a server function, a client action and a query filter alike, and a hot client region containing one still compiles to JavaScript — JavaScript's bitwise operators are specified over the same 32-bit conversion, so the compiled form is the operator itself with nothing added.
Examples#
Packing and unpacking a colour, which is what per-pixel graphics code spends its time on:
int Shade(int colour, int percent) {
int r = (colour >> 16) & 255;
int g = (colour >> 8) & 255;
int b = colour & 255;
return ((r * percent / 100) << 16) | ((g * percent / 100) << 8) | (b * percent / 100);
}A flags value, set, tested and cleared:
int None() { return 0; }
int Read() { return 1; }
int Write() { return 2; }
int Admin() { return 4; }
int Grant(int flags, int bit) { return flags | bit; }
int Revoke(int flags, int bit) { return flags & ~bit; }
bool Has(int flags, int bit) { return (flags & bit) != 0; }
int Toggle(int flags, int bit) { return flags ^ bit; }Overflow is not an error here, and the sign survives a right shift:
int Smallest() { return 1 << 31; } // -2147483648, not an overflow
int NoOpShift() { return 1 << 32; } // 1 — the count masks to five bits
int Halve() { return -8 >> 1; } // -4 — the sign is preservedBoth of these are refused:
double d = 2.5;
int bad = d & 255; // REFUSED — a double has no bit pattern
bool a = true, b = false;
bool also = a & b; // REFUSED — use `&&`; C#'s bool `&` is not implementedSee also#
- Every type, in one list — the scalar types, and which arithmetic is checked
- long — the wider integer type, which these operators do not accept
- Constant expressions — where a value has to be known at compile time