Skip to content

Block Shift Right

Level: 201 · for anyone with a hex editor open

One line: Block Shift Right moves the whole range as one long number the other way, so bits that pass the bottom of one value enter the value after it — which makes Block Shift Right 8 insert a zero byte at the front of the range and push its last byte out, and leaves open what should enter at the top of a signed range.

What the dialog does

The manual describes it as Shift Right on the data "treated as one long block", with what is shifted off X[i] shifted onto X[i+1] (Hex Operations). It is Block Shift Left in the opposite direction, and it inherits both of its questions — how wider values are laid end to end — and one of Shift Right's: what comes in at the top.

In Python

Verified output of block_shift_right_py.py — regenerated by tools/run_examples.py, never hand-typed.

1. THE SAME LONG NUMBER, MOVED THE OTHER WAY
------------------------------------------------------------------------
   12 34 56 as Unsigned Byte, shifted right by 4:

     Shift Right 4         01 03 05   each byte on its own
     Block Shift Right 4   01 23 45   the three bytes as one number

2. BLOCK SHIFT RIGHT 8 INSERTS A ZERO BYTE AND DROPS THE LAST ONE
------------------------------------------------------------------------
     12 34 56            -> 00 12 34
     63 61 66 c3 a9      -> 00 63 61 66 c3   café, now not UTF-8

   The range keeps its size, so inserting a byte at the front pushes one
   out at the back. In café the byte pushed out was half of the é, and
   the byte pushed in is a NUL.

3. RIGHT 4, THEN LEFT 4, LOSES THE LAST DIGIT
------------------------------------------------------------------------
     12 34 56   Block Shift Right 4 -> 01 23 45
     01 23 45   Block Shift Left 4  -> 12 34 50

4. WHAT ENTERS AT THE TOP OF A SIGNED RANGE
------------------------------------------------------------------------
   f0 00, Block Shift Right 4. Its first bit is 1, which as a Signed
   type is a minus sign. Two candidates for what comes in:

     zeros, as this program does          0f 00
     copies of the sign bit, as >> does   ff 00

   The manual says only that Block Shift Right is similar to Shift
   Right, and Shift Right itself leaves the signed case to the
   implementation. Neither row has been measured against the dialog.

The same long number, moved the other way

Section 1 shifts 12 34 56 right by 4. Shift Right on each byte writes 01 03 05, losing every byte's low digit. Block Shift Right writes 01 23 45: each low digit moved into the byte after it, and only the last one left the range.

Block Shift Right 8 inserts a byte

Eight bits is a byte, so Block Shift Right 8 inserts a 00 at the front of the range and pushes the last byte out of the back, keeping the size: 12 34 56 becomes 00 12 34. Section 2 does it to café, and both ends of the range show what that costs in a text file. A NUL now opens the range, and the byte pushed out was the second half of the é, so its lead byte C3 is left at the end with nothing after it and the result is not UTF-8. Section 3 is the round trip: Block Shift Right 4 and then Block Shift Left 4 gives 12 34 50, with the last digit gone.

What enters at the top of a signed range

Section 4 takes F0 00, whose first bit is 1, and asks what comes in when it moves right by 4. The program brings in zeros and writes 0F 00. If the range were read as signed, the first bit would be a minus sign, and the rule Shift Right commonly uses for a negative value copies it in, writing FF 00. The manual says only that Block Shift Right is similar to Shift Right, and Shift Right itself leaves the signed case to the implementation — so both rows are candidates, and neither has been measured against the dialog.

What the manual does not say

  • Whether a Signed type brings in zeros or copies of the sign bit — section 4's two rows.
  • How values wider than a byte are laid end to end in a little-endian file; Block Shift Left prints the two readings.

If you are coming from Python or ABAP

Python. (int.from_bytes(data, 'big') >> n).to_bytes(len(data), 'big') is Block Shift Right over Unsigned Bytes, and the signed row of section 4 is one argument away: int.from_bytes(data, 'big', signed=True) >> n, converted back with signed=True, copies the sign bit in, because Python's >> on a negative number does.

ABAP. (Not machine-checked — CI cannot run ABAP.) For whole bytes, SHIFT hex BY n PLACES RIGHT IN BYTE MODE moves a byte field's content right, and fills the places it frees in a field of fixed length with hexadecimal 0 ↗ — section 2's Block Shift Right 8, with zeros coming in, since a byte field has no sign to copy. On an xstring the same statement lengthens the field instead of pushing its last byte out. For other counts, SET BIT and GET BIT count positions from the start of the field ↗, and a loop over them has to decide what enters at the top, which is section 4's question.

Try it

  1. Type 12 34 56, Block Shift Right 4 as Unsigned Byte, and on a copy Shift Right 4. Compare both with section 1.
  2. In a copy of a UTF-8 file, Block Shift Right 8 over one line, then look at both ends of the selection in the text column.
  3. Type F0 00, set Treat Data As to Signed Byte, Block Shift Right 4, and see which of section 4's rows the dialog wrote.
  4. Block Shift Right 4 and then Block Shift Left 4, and find the digit that did not come back.

See also