Shift operator, unexpected result

tsbockman thomas.bockman at gmail.com
Wed Jun 9 22:03:33 UTC 2021


On Wednesday, 9 June 2021 at 19:13:10 UTC, JG wrote:
> I found the following behaviour, as part of a more complicated 
> algorithm, unexpected. The program:
>
>     import std;
>     void main()
>     {
>         int n = 64;
>         writeln(123uL>>n);
>     }
>
> produces:
>
> 123
>
> I would expect 0.
>
> What is the rationale for this behaviour or is it a bug?

Because it is a high-performance systems programming language, 
the designers of D decided to make the arithmetic operations of 
basic types map directly to the arithmetic operations built in to 
the CPU; most operations are a single instruction.

The benefit of this is higher performance and smaller binaries. 
The disadvantage is that the behaviour of the built in CPU 
operations sometimes differs from ordinary arithmetic in 
surprising and frustrating ways.

If you want to trade a some speed for correctness/predictability, 
try my `checkedint` Dub package. Either way, take a glance at the 
[introduction to the 
documentation](https://checkedint.dpldocs.info/checkedint.html), 
where I list some of the quirks of CPU integer behaviour.

For bit shifts, specifically, many CPUs ignore all but the bottom 
`log2(T.sizeof * 8)` bits of the right-hand operand. 
(`core.bitop.bsr` can be used to do very fast integer `log2` 
operations, and works in CTFE.) Thus, `a >> b` behaves like `a >> 
(b & c)`, where `c` is `(T.sizeof * 8) - 1`.

For unsigned types, the behaviour that you very reasonably expect 
requires two additional instruction on x86, which looks like 
this: `(b <= c)? (a >> b) : 0`. (This should be branchless thanks 
to the `cmov` instruction.)

For signed types, some additional work is required to handle 
negative shifts; see my `checkedint` package.


More information about the Digitalmars-d-learn mailing list