まず、下の関数を見てください。
unsigned positive_modulo(int n, unsigned m){ return (n%m+m)%m; }
これは、整数nと自然数mについて正の剰余数を計算する関数です。
さて、これをn=-20..20、m=7について実行するとどうなるでしょうか。
#includeunsigned positive_modulo(int n, unsigned m); int main(int argc, char* argv[]) { for (int i=-20; i<20; ++i) { std::cout << positive_modulo(i,7) << ", "; } std::cout << std::endl; return 0; }
計算結果(bcc55)
5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5,
あれれ?うまくいった?
いえいえ、よくみると、{0,1,2,3,0,1,2,..}と
3から0に飛んでいる箇所があります。
元の数値は-1, から 0 になるところです。
ちょっと戸惑ったのですが、どうも、符号付きと符号なし整数の演算で起こるようです。
もしn, m がともにint型なら
(1)n%mの結果がintになり、
(2)n%m+mもint型になり、
(3)(n%m+m)%mもint型の期待した答えを得ます。
ちゃんと調べてないのですが、
mがunsignedの場合、(2)の式の評価の中でn%mがintからunsignedへと
暗黙の型変換がおこなわれているようで、ここでアンダーフローのばかでかい数値になっているようです。
私はunsignedからintに変換されると思い込んでいたので、バグになりました。
まとめ: 符号付きありと無しは、比較演算だけでなく、
算術演算でも事前に明示的に型をそろえておくとよい。