Введение
Оператор сдвига в компьютерном программировании
+ Arithmetic shift operators in various programming languages and processors Language or processor Left Right ActionScript 3, Java, JavaScript, Python, PHP, Ruby, C, C++,D, C#, Go, Julia,Rust (signed types only),Swift (signed types only) << >> Ada Shift Left Shift Right Arithmetic Kotlin shl shr Fortran SHIFTL SHIFTA Standard ML << ~>> Verilog <<< >>> OpenVMS macro language @ Scheme arithmetic shift Common Lisp ash OCaml lsl asr Haskell Data. Bits. shift VHDL sla sra Assembly: Z80 SLA SRA Assembly: x86 SAL SAR Assembly: 68k ASL ASR Assembly: RISC V sll, slli sra, srai
In computer programming, an arithmetic shift is a shift operator, sometimes termed a signed shift (though it is not restricted to signed operands). The two basic types are the arithmetic left shift and the arithmetic right shift. For binary numbers it is a bitwise operation that shifts all of the bits of its operand; every bit in the operand is simply moved a given number of bit positions, and the vacant bit positions are filled in. Instead of being filled with all 0s, as in logical shift, when shifting to the right, the leftmost bit (usually the sign bit in signed integer representations) is replicated to fill in all the vacant positions (this is a kind of sign extension). Some authors prefer the terms sticky right shift and zero fill right shift for arithmetic and logical shifts respectively. Arithmetic shifts can be useful as efficient ways to perform multiplication or division of signed integers by powers of two. Shifting left by n bits on a signed or unsigned binary number has the effect of multiplying it by 2n. Shifting right by n bits on a two's complement signed binary number has the effect of dividing it by 2n, but it always rounds down (towards negative infinity). This is different from the way rounding is usually done in signed integer division (which rounds towards 0). This discrepancy has led to bugs in a number of compilers. For example, in the x86 instruction set, the SAR instruction (arithmetic right shift) divides a signed number by a power of two, rounding towards negative infinity. However, the IDIV instruction (signed divide) divides a signed number, rounding towards zero. So a SAR instruction cannot be substituted for an IDIV by power of two instruction nor vice versa.
+ Операторы арифметического сдвига в различных языках программирования и процессорах
Язык или процессор Левый Правый
ActionScript 3, Java, JavaScript, Python, PHP, Ruby, C, C++, D, C#, Go, Julia, Rust (только для знаковых типов), Swift (только для знаковых типов) << >>
Ada Сдвиг влево Сдвиг вправо Арифметический
Kotlin shl shr
Fortran SHIFTL SHIFTA
Standard ML << ~>>
Verilog <<< >>>
OpenVMS макро язык @
Scheme арифметический сдвиг
Common Lisp ash
OCaml lsl asr
Haskell Data.Bits.shift
VHDL sla sra
Assembly: Z80 SLA SRA
Assembly: x86 SAL SAR
Assembly: 68k ASL ASR
Assembly: RISC V sll, slli sra, srai
+ Arithmetic shift operators in various programming languages and processors Language or processor Left Right ActionScript 3, Java, JavaScript, Python, PHP, Ruby, C, C++,D, C#, Go, Julia,Rust (signed types only),Swift (signed types only) << >> Ada Shift Left Shift Right Arithmetic Kotlin shl shr Fortran SHIFTL SHIFTA Standard ML << ~>> Verilog <<< >>> OpenVMS macro language @ Scheme arithmetic shift Common Lisp ash OCaml lsl asr Haskell Data. Bits. shift VHDL sla sra Assembly: Z80 SLA SRA Assembly: x86 SAL SAR Assembly: 68k ASL ASR Assembly: RISC V sll, slli sra, srai
In computer programming, an arithmetic shift is a shift operator, sometimes termed a signed shift (though it is not restricted to signed operands). The two basic types are the arithmetic left shift and the arithmetic right shift. For binary numbers it is a bitwise operation that shifts all of the bits of its operand; every bit in the operand is simply moved a given number of bit positions, and the vacant bit positions are filled in. Instead of being filled with all 0s, as in logical shift, when shifting to the right, the leftmost bit (usually the sign bit in signed integer representations) is replicated to fill in all the vacant positions (this is a kind of sign extension). Some authors prefer the terms sticky right shift and zero fill right shift for arithmetic and logical shifts respectively. Arithmetic shifts can be useful as efficient ways to perform multiplication or division of signed integers by powers of two. Shifting left by n bits on a signed or unsigned binary number has the effect of multiplying it by 2n. Shifting right by n bits on a two's complement signed binary number has the effect of dividing it by 2n, but it always rounds down (towards negative infinity). This is different from the way rounding is usually done in signed integer division (which rounds towards 0). This discrepancy has led to bugs in a number of compilers. For example, in the x86 instruction set, the SAR instruction (arithmetic right shift) divides a signed number by a power of two, rounding towards negative infinity. However, the IDIV instruction (signed divide) divides a signed number, rounding towards zero. So a SAR instruction cannot be substituted for an IDIV by power of two instruction nor vice versa.
В компьютерном программировании арифметический сдвиг — это оператор сдвига, иногда называемый знаковым сдвигом (хотя он не ограничивается знаковыми операндами). Существуют два основных типа: арифметический сдвиг влево и арифметический сдвиг вправо. Для двоичных чисел это побитовая операция, которая сдвигает все биты операнда; каждый бит в операнде просто перемещается на заданное количество бит-позиций, а освободившиеся бит-позиции заполняются. Вместо заполнения нулями, как в логическом сдвиге, при сдвиге вправо самый левый бит (обычно знаковый бит в представлениях целых чисел со знаком) реплицируется для заполнения всех освободившихся позиций (это своего рода расширение знака). Некоторые авторы предпочитают термины «сдвиг вправо с переносом знака» и «сдвиг вправо с заполнением нулями» для арифметических и логических сдвигов соответственно. Арифметические сдвиги могут быть полезны как эффективный способ выполнения умножения или деления целых чисел со знаком на степени двойки. Сдвиг влево на n битов для знакового или беззнакового двоичного числа эквивалентен умножению на 2n. Сдвиг вправо на n битов для двоичного числа в дополнительном коде эквивалентен делению на 2n, но всегда округляется вниз (к отрицательной бесконечности). Это отличается от способа округления, обычно используемого при делении целых чисел со знаком (которое округляется к 0). Это несоответствие привело к ошибкам в ряде компиляторов. Например, в наборе инструкций x86 инструкция SAR (арифметический сдвиг вправо) делит знаковое число на степень двойки, округляя в сторону отрицательной бесконечности. Однако инструкция IDIV (деление со знаком) делит знаковое число, округляя к нулю. Следовательно, инструкцию SAR нельзя заменить инструкцией IDIV, умноженной на степень двойки, и наоборот.
+ Arithmetic shift operators in various programming languages and processors Language or processor Left Right ActionScript 3, Java, JavaScript, Python, PHP, Ruby, C, C++,D, C#, Go, Julia,Rust (signed types only),Swift (signed types only) << >> Ada Shift Left Shift Right Arithmetic Kotlin shl shr Fortran SHIFTL SHIFTA Standard ML << ~>> Verilog <<< >>> OpenVMS macro language @ Scheme arithmetic shift Common Lisp ash OCaml lsl asr Haskell Data. Bits. shift VHDL sla sra Assembly: Z80 SLA SRA Assembly: x86 SAL SAR Assembly: 68k ASL ASR Assembly: RISC V sll, slli sra, srai
In computer programming, an arithmetic shift is a shift operator, sometimes termed a signed shift (though it is not restricted to signed operands). The two basic types are the arithmetic left shift and the arithmetic right shift. For binary numbers it is a bitwise operation that shifts all of the bits of its operand; every bit in the operand is simply moved a given number of bit positions, and the vacant bit positions are filled in. Instead of being filled with all 0s, as in logical shift, when shifting to the right, the leftmost bit (usually the sign bit in signed integer representations) is replicated to fill in all the vacant positions (this is a kind of sign extension). Some authors prefer the terms sticky right shift and zero fill right shift for arithmetic and logical shifts respectively. Arithmetic shifts can be useful as efficient ways to perform multiplication or division of signed integers by powers of two. Shifting left by n bits on a signed or unsigned binary number has the effect of multiplying it by 2n. Shifting right by n bits on a two's complement signed binary number has the effect of dividing it by 2n, but it always rounds down (towards negative infinity). This is different from the way rounding is usually done in signed integer division (which rounds towards 0). This discrepancy has led to bugs in a number of compilers. For example, in the x86 instruction set, the SAR instruction (arithmetic right shift) divides a signed number by a power of two, rounding towards negative infinity. However, the IDIV instruction (signed divide) divides a signed number, rounding towards zero. So a SAR instruction cannot be substituted for an IDIV by power of two instruction nor vice versa.
Эквивалентность арифметических и логических левых смещений и умножения
Арифметический сдвиг влево эквивалентен умножению на (положительную, целую) степень основания системы счисления (например, умножение на степень 2 для двоичных чисел). Логический сдвиг влево также эквивалентен, однако умножение и арифметический сдвиг могут привести к арифметическому переполнению, чего не происходит при логическом сдвиге.
Неэквивалентность арифметического смещения вправо и деления
Однако арифметический сдвиг вправо является серьезной ловушкой для невнимательных, особенно в отношении округления отрицательных целых чисел. Например, в стандартном представлении отрицательных целых чисел в дополнительном коде, −1 представляется как все единицы. Для 8-битного знакового целого числа это 1111 1111. Арифметический сдвиг вправо на 1 (или 2, 3, …, 7) снова дает 1111 1111, что по-прежнему равно −1. Это соответствует округлению вниз (к отрицательной бесконечности), но не является общепринятой конвенцией для деления. Часто утверждается, что арифметический сдвиг вправо эквивалентен делению на (положительную, целую) степень основания системы счисления (например, делению на степень 2 для двоичных чисел), и, следовательно, деление на степень основания можно оптимизировать, реализовав его как арифметический сдвиг вправо. (Сдвигающее устройство намного проще, чем делитель. На большинстве процессоров инструкции сдвига выполняются быстрее, чем инструкции деления.) Многие руководства по программированию, справочники и другие спецификации 1960-х и 1970-х годов, выпущенные такими компаниями и организациями, как DEC, IBM, Data General и ANSI, содержат подобные неверные утверждения. Логический сдвиг вправо эквивалентен делению на степень основания (обычно 2) только для положительных или беззнаковых чисел. Арифметический сдвиг вправо эквивалентен логическому сдвигу вправо для положительных знаковых чисел. Арифметический сдвиг вправо для отрицательных чисел в дополнительном коде (обычно в дополнительном коде двойной точности) приблизительно эквивалентен делению на степень основания (обычно 2), при этом для нечетных чисел применяется округление вниз (а не к 0, как обычно ожидается). Арифметический сдвиг вправо для отрицательных чисел эквивалентен делению с округлением к 0 в прямом дополнительном коде знаковых чисел, который использовался в некоторых старых компьютерах, но в настоящее время практически не применяется.
Решение проблемы в языках программирования
Стандарт ISO 1999 года для языка программирования C определяет оператор правого сдвига в терминах деления на степени 2. В связи с вышеупомянутой неэквивалентностью, стандарт явно исключает из этого определения правый сдвиг знаковых чисел с отрицательными значениями. Он не специфицирует поведение оператора правого сдвига в таких случаях, а вместо этого требует от каждого отдельного компилятора C определить поведение при правом сдвиге отрицательных значений. Как и в C, в C++ поведение правого сдвига знаковых целых чисел определялось реализацией до C++20. Начиная со стандарта C++20, правый сдвиг знакового целого числа определяется как арифметический сдвиг.
Приложения
В тех случаях, когда требуется последовательное округление вниз, полезны арифметические сдвиги вправо для знаковых чисел. Пример – уменьшение масштаба растровых координат в степени двойки, что обеспечивает равномерный интервал. Например, сдвиг вправо на 1 преобразует 0, 1, 2, 3, 4, 5 в 0, 0, 1, 1, 2, 2, а −1, −2, −3, −4 в −1, −1, −2, −2, сохраняя равномерный интервал, как −2, −2, −1, −1, 0, 0, 1, 1, 2, 2. В отличие от этого, целочисленное деление с округлением к нулю преобразует −1, 0 и 1 все в 0 (3 значения вместо 2), в результате чего получается −2, −1, −1, 0, 0, 0, 1, 1, 2, 2, что является неравномерным в точке 0.