Этот вопрос вдохновлен моими попытками ответить на другой вопрос: Преобразование десятичного/целочисленного в двоичный - как и почему он работает так, как он делает?
Только документация для операторов побитового сдвига, которые я могу найти, говорит:
Операции x shl y и x shr y сдвигают значение x влево или вправо на y битов, которое (если x является целым без знака) эквивалентно умножению или делению x на 2 ^ y; результат имеет тот же тип, что и x. Например, если N хранит значение 01101 (десятичное число 13), тогда N shl 1 возвращает 11010 (десятичное значение 26). Обратите внимание, что значение y интерпретируется по модулю размера типа x. Так, например, если x является целым числом, x shl 40 интерпретируется как x shl 8, потому что целое число равно 32 битам, а 40 mod 32 равно 8.
Рассмотрим эту программу:
{$APPTYPE CONSOLE}
program BitwiseShift;
var
u8: Byte;
u16: Word;
u32: LongWord;
u64: UInt64;
begin
u8 := $ff;
Writeln((u8 shl 7) shr 7);
// expects: 1 actual: 255
u16 := $ffff;
Writeln((u16 shl 15) shr 15);
// expects: 1 actual: 65535
u32 := $ffffffff;
Writeln((u32 shl 31) shr 31);
// expects: 1 actual: 1
u64 := $ffffffffffffffff;
Writeln((u64 shl 63) shr 63);
// expects: 1 actual: 1
end.
Я запустил это как с XE3, так и с XE5, как для 32-разрядных, так и для 64-битных компиляторов Windows, а исходящие из них непротиворечивы, как указано в коде выше.
Я ожидал, что (u8 shl 7) shr 7
будет полностью оцениваться в контексте 8-битного типа. Поэтому, когда биты сдвинуты за конец этого 8-битного типа, эти биты теряются.
Мой вопрос в том, почему программа ведет себя так же, как и она.
Интересно, что я перевел программу на С++, и на моем 64-битном средстве 4.6.3 был получен тот же результат.
#include <cstdint>
#include <iostream>
int main()
{
uint8_t u8 = 0xff;
std::cout << ((u8 << 7) >> 7) << std::endl;
uint16_t u16 = 0xffff;
std::cout << ((u16 << 15) >> 15) << std::endl;
uint32_t u32 = 0xffffffff;
std::cout << ((u32 << 31) >> 31) << std::endl;
uint64_t u64 = 0xffffffffffffffff;
std::cout << ((u64 << 63) >> 63) << std::endl;
}