符号つき整数と符号なし整数が混合した式の評価(implicit type conversion)

今回は、剰余計算でのバグについてです。
まず、下の関数を見てください。

unsigned positive_modulo(int n, unsigned m){
   return (n%m+m)%m;
}

これは、整数nと自然数mについて正の剰余数を計算する関数です。
さて、これをn=-20..20、m=7について実行するとどうなるでしょうか。

#include 

unsigned 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に変換されると思い込んでいたので、バグになりました。

まとめ: 符号付きありと無しは、比較演算だけでなく、
算術演算でも事前に明示的に型をそろえておくとよい。