7

私は、最初に1ではなく0を追加するような左シフトを実行しようとしています。たとえば、Shiftを左にすると0xff、次のようになります。

0xff << 3 = 11111000

ただし、右にシフトすると、次のようになります。

0xff >> 3 = 11111111

左シフトに相当するものを取得するために使用できる操作はありますか?つまり、私はこれを取得したいと思います:

00011111

なにか提案を?

編集

コメントに答えるために、私が使用しているコードは次のとおりです。

int number = ~0;
number = number << 4;   
std::cout << std::hex << number << std::endl;

number = ~0;
number = number >> 4;
std::cout << std::hex << number << std::endl;

出力:

fffffff0
ffffffff

一般的には機能するはずなので、この特定のコードが機能しない理由に興味があります。何か案が?

4

6 に答える 6

10

これは、C と 2 進演算の両方がどのように機能するかです。

Shift を左0xff << 3にすると、バイナリが得られます。00000000 11111111 << 3 = 00000111 11111000

を右にシフト0xff >> 3すると、バイナリが得られます。00000000 11111111 >> 3 = 00000000 00011111

0xff正の値を持つ (符号付き) int です255。これは正であるため、シフトの結果は、C と C++ の両方で明確に定義された動作になります。算術シフトも、種類や定義が不十分な動作も行いません。

#include <stdio.h>

int main()
{

  printf("%.4X %d\n", 0xff << 3, 0xff << 3);
  printf("%.4X %d\n", 0xff >> 3, 0xff >> 3);

}

出力:

07F8 2040
001F 31

つまり、期待どおりに動作しないため、プログラムで奇妙なことをしているということです。char 変数または C++ 文字リテラルを使用している可能性があります。


出典: ISO 9899:2011 6.5.7.


質問の更新後に編集

int number = ~0;2 の補数を仮定すると、-1 に相当する負の数が得られます。

number = number << 4;負の数を左シフトしたため、未定義の動作が発生します。プログラムは未定義の動作を正しく実装します。これは、何かを実行するか、まったく実行しないためです。fffffff0 を出力するか、ピンクの象を出力するか、ハード ドライブをフォーマットする可能性があります。

number = number >> 4;実装定義の動作を呼び出します。あなたの場合、コンパイラは符号ビットを保持します。これは算術シフトと呼ばれ、算術右シフトは、MSB がシフト前のビット値で満たされるように機能します。したがって、負の数を持っている場合、プログラムが「1 にシフト」していることを経験します。

実世界のすべてのケースの 99% では、符号付き数値にビット単位の演算子を使用しても意味がありません。したがって、常に符号なしの数値を使用していること、および C/C++ の危険な暗黙の変換規則のいずれによっても符号付き数値に変換されていないことを確認してください (危険な変換の詳細については、「整数昇格規則」および「通常の算術変換」を参照してください)。 "、SOに関する多くの良い情報)。

EDIT 2、C99標準の根拠文書V5.10からの情報:

6.5.7 ビットシフト演算子

K&R のシフト演算子の説明では、長いカウントでシフトすると、シフトされる前に左側のオペランドが長く拡張される必要があることが示唆されています。C89 委員会によって承認されたより直感的な方法は、シフト カウントの型が結果の型に関係しないというものです。

C89の静かな変化

long カウントでシフトしても、シフトされたオペランドが long に強制されなくなりました。C89 委員会は、K&R によって付与された実装の自由を確認し、符号付き右シフト操作を符号拡張する必要はありません。そのような要件は高速コードを遅くする可能性があり、符号拡張シフトの有用性はわずかであるためです。(負の 2 の補数の整数を算術的に右に 1 桁シフトすることは、2 で割ることと同じではありません!)

于 2013-01-18T10:52:03.103 に答える
8

明示的に 0xff をシフトすると、期待どおりに動作します

cout << (0xff >> 3) << endl; // 31

0xff符号付き幅 8 のタイプ (charおよびsigned char一般的なプラットフォーム)の場合にのみ可能です。


したがって、一般的なケースでは:

unsigned int を使用する必要があります

(unsigned type)0xff

右シフトは2による除算として機能します(正しく理解できれば切り捨てます)。

したがって、最初のビットが 1 の場合、の値があり、除算後は再びになります。

于 2013-01-18T10:27:15.947 に答える
5

あなたが話している右シフトの 2 種類は、Logical ShiftArithmetic Shiftと呼ばれます。C および C++ は符号なし整数に論理シフトを使用し、ほとんどのコンパイラは符号付き整数に算術シフトを使用しますが、これは標準の意味では保証されていません。つまり、負の符号付き整数を右にシフトする値は実装定義です。

論理シフトが必要なため、符号なし整数の使用に切り替える必要があります。これを行うには、定数を に置き換えます0xffU

于 2013-01-18T10:36:39.813 に答える
3

実際のコードを説明するには、Lundin がコメントで提供した C 標準からの引用の C++ バージョンが必要です。

int number = ~0;
number = number << 4;

未定義の動作。[expr.shift] 言う

E1 << E2 の値は、E1 を左にシフトした E2 ビット位置です。空いたビットはゼロで埋められます。E1 が unsigned 型の場合、結果の値は E1 × 2 E2であり、結果の型で表現可能な最大値より 1 を法として減じられます。それ以外の場合、E1 が符号付きの型で負でない値を持ち、E1×2 E2が結果の型で表現可能である場合、それが結果の値になります。それ以外の場合、動作は未定義です。

number = ~0;
number = number >> 4;

実装定義の結果。この場合、実装によって算術シフトが行われます。

E1 >> E2 の値は、E1 を右シフトした E2 ビット位置です。E1 が unsigned 型の場合、または E1 が signed 型で負でない値の場合、結果の値は E1/2 E2の商の整数部分です。E1 に符号付きの型と負の値がある場合、結果の値は実装定義です。

署名されていない型を使用する必要があります。

unsigned int number = -1;
number = number >> 4;
std::cout << std::hex << number << std::endl;

出力:

0x0fffffff
于 2013-01-18T12:58:47.350 に答える
0

念のため、右シフト後に負の数の最初のビットを 0 にしたい場合、MSB をゼロにする INT_MIN を使用してその負の数の XOR を取ることができます。これは適切な算術シフトではないことを理解していますが、仕事を片付ける

于 2021-04-14T13:46:41.123 に答える