# Bitwise operators

> `&`, `|`, `^`, `~`, `<<` and `>>` work on `int`, with C#'s meanings and C#'s precedence. Unlike `+`, `-`, `*` and `/`, they do not raise on overflow — a bit operation is defined modulo 2^32, so `1 << 31` is a negative number rather than an error. The operands must be integers.

<!-- id: types-bitwise-operators · area: types · stability: stable · html: https://osysharp.com/reference/types/bitwise-operators/ -->

## Summary        {#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      {#signature}

```osy syntax
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 preserved
```

## Description    {#description}

### They are unchecked   {#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   {#shift-count}

`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   {#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   {#precedence}

From loosest to tightest:

```text
||   <   &&   <   |   <   ^   <   &   <   ==  !=   <   <  <=  >  >=   <   <<  >>   <   +  -   <   *  /  %
```

Two consequences catch people out in C too, and they are the same here:

- `a & b == c` is `a & (b == c)` — equality binds **tighter** than `&`.
- `1 << 2 + 1` is `1 << 3`, which is `8` — 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   {#operands}

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   {#execution-side}

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       {#examples}

Packing and unpacking a colour, which is what per-pixel graphics code spends its time on:

```osy title="scale the three channels of a packed colour" test app=types-bitwise
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:

```osy title="flags" test app=types-bitwise
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:

```osy title="the two rules that differ from arithmetic" test app=types-bitwise
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 preserved
```

Both of these are refused:

```osy title="✗ a double has no bits, and a bool wants &&" syntax
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 implemented
```

## See also       {#see-also}
- [Every type, in one list](https://osysharp.com/reference/types/vocabulary/) — the scalar types, and which arithmetic is checked
- [long](https://osysharp.com/reference/types/long/) — the wider integer type, which these operators do not accept
- [Constant expressions](https://osysharp.com/reference/types/constant-expressions/) — where a value has to be known at compile time
