2014/09/09

[java]符号無し演算にはまる

プロトコル解析するときは、だいたい符号無し1byteで扱っていって、必要があれば符号有りにしたりすると思う。
いつも忘れるのだが、Javaは符号無し、が無い。
"byte"すらも、符号付き1byte型だ。
なんとなく話の方向の想像がついただろうが、まあ書かせておくれ。

 

C#で書いて動いているFeliCa Lite-Sの相互認証処理を、Androidに移植している。
実装は終わってデバッグしているのだが、どうにもMAC(W)計算が合わない。
半日あれこれ調べたところ、書込カウント値が正しく計算できていないことに気付いた。。

Lite-SからはMACが強化されて、書込カウント値を使うようになった。
これが24bit(3byte)ある。
Read without EncryptionでWCNTレジスタを読込み、その中の3byteを整数変換するだけだ。
C#では、こんな感じで書いている。

int wcnt = rbuf[0] | (rbuf[1] << 8) | (rbuf[2] << 16);

rbufはbyte[]なんだけど、C#のbyteは符号無し1byteだ。
だから、ビットシフトしても符号無しのままでいてくれる。

ええ、Javaでも同じように書いてますよ。
このときのrbufは、こんな値だった。

rbuf[0] = 0x85
rbuf[1] = 0xFE
rbuf[2] = 0xFF

リトルエンディアンなので、0xFFFE85、という値になることを期待していた。
していたのだが、Javaでは0xFFFF85になっていたのだ!
なぜなぜ??

デバッグのため、int aaにrbuf[0],、bbにrbuf[1] << 8、ccにrbuf[2] << 16を突っ込んで確認した。

image

1byteの符号付き0x85が、符号付きint型に拡張される際に、最上位ビットが立っているので「これはマイナス値だ!」と判断して、そっから先のビットを全部1にしてしまったのだな。。。

Cでたまにやらかすので、心配せんでいいように符号無しで扱う習慣なんだけど、Javaだと記述で解決せんといかんのか。

int wcnt = (rbuf[32] & 0x0000ff | (rbuf[33] << 8) & 0x00ff00 | (rbuf[34] << 16) & 0xff0000) & 0xffffff;

ビットシフトして型が大きい方に膨らむ場合は、ほしい分だけ論理積を取るのが安全か。
小さい方に代入するときは勝手に消えるからいいだろう。
まあ、byte型の-1をint型にキャストして255になったら困るけど、それならunsignedがほしくなる。


大刷新リリース Java 8の新機能 - 符号なし整数のサポート
http://news.mynavi.jp/special/2014/java8/011.html

おおおお?
とうとうunsignedが使えるようになるんだ!
・・・と思ったが・・・・・・

Integer.toUnsignedString(a)
あれ?
こっちも「Unsigned Integer API」って書いてる。
http://www.informit.com/articles/article.aspx?p=2216988&seqNum=2
違うんだ、私がほしいのは"unsigned"なんだーー

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。