Edited at

Unsigned right shift operatorはなぜC/C++にないのか

静大情報AC(IT) 6日目です。

前日(5日目)は欠員です。

前々日(4日目)はasdfg0280氏の「Nuxt.js使ってみた」です。


はじめに

JavaとJVMのSpecificationを引用する際には、それぞれ"Java§1.2.3"、"JVM§4.5.6"のように記述します。

Java/JVMのバージョンは11とします。


Unsigned right shift operator?

Javaにはunsigned right shift operator(unsigned右シフト演算子)なる演算子があります。

int i = -4 >>  1; // -2         (signed right shift)

int j = -4 >>> 1; // 2147483646 (unsigned right shift)

通常のシフトが符号拡張により符号を保存する(算術シフト)のに対し、unsigned右シフトにおいては符号ビットを気にせずそのままシフト(論理シフト)します。(Java§15.19)

C/C++にはないこの演算子、なぜJavaにはあるのでしょうか。


Javaにはunsigned数値型がない

Java§4.2.1によれば、Javaの組み込み整数型は以下の4つです。



  • byte(-128 ~ 127)


  • short(-32768 ~ 32767)


  • int(-2147483648 ~ 2147483647)


  • long(-9223372036854775808 ~ 9223372036854775807)


  • char('\u0000' ~ '\uffff'、すなわち0 ~ 65535)

値の範囲からわかるようにchar以外はすべてsignedです。(JVM§2.3にもcharのみがunsignedとの記述があります)

また、charでさえもシフト演算時には内部的にintに変換されてから演算に使われます。("Unary numeric promotion", Java§5.6.1)

JVM§2.11.1によればJVMのシフト演算命令はintlongにしか定義されていないことから、シフト演算に限って言えば、Javaにはunsigned整数型はないと言えます。


Unsignedなシフトの必要性

さて、unsigned整数型がないとどのように困るのでしょうか。

次のコードを見てみましょう。

String toBits(int val) {

var builder = new StringBuilder();
for(int j = 0; j < Integer.SIZE; j++, val >>>= 1) {
builder.append((val & 1) == 1 ? "1" : "0");
}
return builder.reverse().toString();
}

これは、int型引数を取りそのビット表現をStringで返す関数です。

例:

-1 -> "11111111111111111111111111111111"

20181206 -> "00000001001100111111000011010110"

3行目で>>>=(>>>しつつ代入する演算子)を使っています。

仮にJavaの世界に>>>がなくても、この関数が書けないというわけではありません。

ただ、負の値を算術右シフトすると符号拡張で上位ビットを1が埋めてしまうので、負の数に対しては特別な操作が必要となります。

このように、Javaにおいてはunsigned right shift operatorはなくても死にはしないけれど、あったほうが便利だということがわかりました。


C/C++にはなぜright shift operatorがないか

C/C++では符号なし整数型があり、これらの型に対するシフトは論理シフトになることが規格で定められています。

したがって、right shift operatorがなくても同様の操作を行うことができます。


おわり

翌日(7日目)は欠員です。

翌々日(8日目)はhi97_ia16氏の「Node.jsで大学の成績を自動取得するシステムを開発した話」です。

(両脇に人がいない……)