Javaの符号なし右シフト演算子 ">>>"が何をするのか理解していますが、なぜそれが必要なのか、そして対応する符号なし左シフト演算子が必要ないのはなぜですか?
>>>
演算子を使用すると、int
およびlong
を32ビットおよび64ビットとして扱うことができますnsigned整数型であり、Java言語。
これは、数値を表さないものをシフトするときに役立ちます。たとえば、32ビットのint
sを使用して白黒のビットマップイメージを表すことができます。各int
は画面上の32ピクセルをエンコードします。画像を右にスクロールする必要がある場合は、int
の左側のビットをゼロにして、隣接するint
sのビットを簡単に配置できるようにします。
int shiftBy = 3;
int[] imageRow = ...
int shiftCarry = 0;
// The last shiftBy bits are set to 1, the remaining ones are zero
int mask = (1 << shiftBy)-1;
for (int i = 0 ; i != imageRow.length ; i++) {
// Cut out the shiftBits bits on the right
int nextCarry = imageRow & mask;
// Do the shift, and move in the carry into the freed upper bits
imageRow[i] = (imageRow[i] >>> shiftBy) | (carry << (32-shiftBy));
// Prepare the carry for the next iteration of the loop
carry = nextCarry;
}
上記のコードは、>>>
演算子が上位3ビットの内容を作成するため、上位3ビットの内容に注意を払いません
符号付きデータ型と符号なしデータ型の左シフト演算は同じであるため、対応する<<
演算子はありません。
>>>
もthe2つの(大きい)整数の丸め平均を見つける安全で効率的な方法です。
int mid = (low + high) >>> 1;
整数high
とlow
が最大のマシン整数に近い場合、上記は正しいですが
int mid = (low + high) / 2;
オーバーフローのために誤った結果を得る可能性があります。
これが 使用例 で、単純なバイナリ検索のバグを修正しています。
基本的に、これは符号(数値シフト)または符号なしシフト(通常はピクセル関連のもの)に関係しています。
左シフトなので、とにかく符号ビットを処理しないので、同じことです(<<<と<<)...
どちらの方法でも、>>>を使用する必要のある人にまだ会ったことがありませんが、素晴らしいことをやっていると確信しています。
先ほど見たように、>>演算子は、シフトが発生するたびに、上位ビットを以前の内容で自動的に埋めます。これにより、値の符号が保持されます。ただし、これは望ましくない場合があります。たとえば、数値を表さないものをシフトする場合、符号拡張を行わないでください。この状況は、ピクセルベースの値とグラフィックスで作業している場合に一般的です。これらの場合、初期値が何であろうと、一般にゼロを上位ビットにシフトする必要があります。これは符号なしシフトと呼ばれます。これを行うには、Javaの符号なし右シフト演算子、>>>を使用します。この演算子は常にゼロを上位ビットにシフトします。
参考文献:
http://henkelmann.eu/2011/02/01/Java_the_unsigned_right_shift_operator
符号付き右シフト演算子は、数値を表すint
があり、それを2の累乗で割り、負の無限大に丸める場合に便利です。これは、座標を表示用にスケーリングするような場合に便利です。除算よりも高速であるだけでなく、スケーリングの前にスケールファクタが異なる座標は、後で1ピクセル異なります。シフトを使用する代わりに除算を使用する場合、それは機能しません。たとえば、2倍にスケーリングする場合、-1と+1は2ずつ異なるため、後で1ずつ異なるはずですが、-1/2 = 0と1/2 = 0です。代わりに、符号付き右シフトを使用すると、-1 >> 1 = -1および1 >> 1 = 0となり、1ピクセル離れた値が適切に生成されます。
符号なし演算子は、入力のビットセットが1つだけであることが期待され、結果もそうしたい場合、またはループを使用してWordのすべてのビットを出力する場合に便利です。きれいに終了させたい。例えば:
void processBitsLsbFirst(int n, BitProcessor whatever)
{
while(n != 0)
{
whatever.processBit(n & 1);
n >>>= 1;
}
}
コードが符号付き右シフト演算を使用し、負の値が渡された場合、1を無期限に出力します。ただし、符号なし右シフト演算子を使用すると、最上位ビットは他のビットと同じように解釈されます。
符号なし右シフト演算子は、計算により、算術的に0から4,294,967,295の間の正の数が得られ、その数を2の累乗で除算したい場合にも役立ちます。たとえば、正であることがわかっている2つのint
値の合計を計算する場合、オペランドをlong
に昇格させることなく(n1+n2)>>>1
を使用できます。また、浮動小数点演算を使用せずにint
の正の値をpiのようなもので除算したい場合、((value*5468522205L) >>> 34)
[(1L << 34)/ piは5468522204.61を計算します。は5468522205を返します]。 1686629712を超える被除数の場合、value*5468522205L
の計算は「負の」値を生成しますが、算術的に正しい値は正であることがわかっているため、符号なし右シフトを使用すると、正しい正の数を使用できます。
通常の右シフト>>
が負の数であると、負の数が維持されます。つまり符号ビットは保持されます。
unsigned右シフト>>>
は、符号ビットもシフトし、ゼロビットに置き換えます。
符号ビットは1つしかなく、それが左端のビットであるため、右シフトの場合にのみ干渉するため、同等の左シフトを行う必要はありません。
基本的に、違いは、1つは符号ビットを保持し、もう1つはゼロにシフトして符号ビットを置き換えることです。
正の数の場合、それらは同じように動作します。
両方の使用例>>
および>>>
参照 BigInteger shiftRight 。
Javaドメインで最も一般的なアプリケーションでは、オーバーフローを回避する方法は、前の例のintからlongのようなキャストまたはBig Integerを使用することです。
int hiint = 2147483647;
System.out.println("mean hiint+hiint/2 = " + ( (((long)hiint+(long)hiint)))/2);
System.out.println("mean hiint*2/2 = " + ( (((long)hiint*(long)2)))/2);
BigInteger bhiint = BigInteger.valueOf(2147483647);
System.out.println("mean bhiint+bhiint/2 = " + (bhiint.add(bhiint).divide(BigInteger.valueOf(2))));