大阪市中央区 システムソフトウェア開発会社

営業時間:平日09:15〜18:15
MENU

符号付き整数と符号無し整数の違い

著者:高木信尚
公開日:2018/06/25
最終更新日:2018/06/25
カテゴリー:技術情報

高木です。こんにちは。

最近になって、不覚にもごく初歩的な間違いを犯してしまいました。
自戒と備忘録を兼ねて、その内容を投稿しておきたいと思います。

16ビットの符号無し整数どうしを乗算したあと、0xffffで除算して結果を16ビットに収めようとしました。
そこでやってしまったのです。
プログラミング言語はCです。

int型が32ビットの処理系ですので、0xffffがint型になることはわかっていました。
16ビット符号無し整数、正確にはuint16_t型の整数も整数拡張されてint型になることもわかっていました。

失敗の原因は、乗算する一方のオペランドが最大0xffだと考えていたことです。
確かに最初はそうだったのですが、途中から方針を変えて0xffffが最大になりました。
このときに修正を忘れたんですね。

せっかくの機会なので、符号付き整数と符合無し整数では具体的に何がどう違うかを書いておきたいと思います。
なお、厳密なことをいえば、負値の内部表現は1の補数や符号ビットと絶対値もあるのですが、現実に遭遇することはまずありませんので、今回は2の補数に限った話をします。

加減算に関していえば、符合付き整数と符合無し整数では同じビットパターンが生成されます。
正確には、符合付き整数の場合には、オーバーフローが発生した場合は処理系定義の値になるか、処理系定義のシグナルが発生します。
そして、処理系定義のシグナルが発生した場合の動作は未定義です。
オーバーフローが発生すると実質的に未定義の動作になるわけですが、現実の処理系では何事もなかったかのように符合無し整数の加減算と同じビットパターンが生成されるようです。

乗除算については、符合の有無で明らかに振る舞いが異なります。
符合付きの場合、絶対値どうしで乗除算を行い、その結果に符合を適用することになります。
乗除算の結果、オーバーフローが発生した場合はやはり未定義の動作になると考えるべきです。

今回やってしまった 0xffff * 0xffff でいえば、乗算の結果は 0xfffe0001 になります。
これは符号ビットが1ですのでオーバーフローが発生しています。
とりあえず、このビットパターンが正しいとして10進法に直すと、-131071 になります。
当然負の値になりますね。

そして、この -131071 を 0xffff で除算すると、-2 になります。
16進法では 0xfffffffe です。
下位16ビットを取ると 0xfffe になり、期待した 0xffff にはなりませんね。

ほかにも、符合付き整数と符合無し整数の違いが影響してやってしまうケースがいくつかあります。

たとえば、右シフトもそうです。
符合無し整数の場合、右シフトと2の累乗での除算は結果が同じになります。
しかし、符合付き整数の場合は違います。

一番わかりやすいのは -1 >> 1 と -1 / 2 ではないでしょうか?
ここでも、正確にいえば負値を右シフトした場合の結果は処理系定義なのですが、現実の処理系では算術シフトになりますのでその前提で話しています。
-1 >> 1 の結果は -1 になりますが、-1 / 2 の結果は 0 になりますね。
もし仮に負値の内部表現が1の補数であれば、-1 >> 1 は 0(正確には -0)になるかと思います。

左シフトもやってしまいますね。
左オペランドが符合付き整数の場合、左シフトが有効なのは、左オペランドが非負の値を持ち、左オペランド × 2右オペランド が結果の型で表現可能な場合だけです。
それ以外は未定義の動作になります。

もっとほかにもある気がしたのですが、よく考えてみると、それらはトラップ表現が絡む場合など、現実には遭遇しそうにないケースだけでした。
というわけで、わかっていてもやらかしてしまうミスはあるということです。

    上に戻る