Thread overview
Shift operator, unexpected result
Jun 09, 2021
JG
Jun 09, 2021
kinke
Jun 09, 2021
tsbockman
Jun 10, 2021
Kagamin
June 09, 2021

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?

June 09, 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?

This is undefined behavior as in other languages, see ยง3 in https://dlang.org/spec/expression.html#shift_expressions.

June 09, 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, 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.

June 10, 2021

On Wednesday, 9 June 2021 at 19:13:10 UTC, JG wrote:

>

produces:

123

I would expect 0.

What is the rationale for this behaviour or is it a bug?

Processor just takes lower 6 bits for the shift amount and those hold zero in your case, shifting by 65 will shift by 1.