【C言語】負の値に対する剰余演算の結果まとめ

負の値に対する剰余演算の結果まとめページのアイキャッチ

あるプログラムを作っていて、”負の値に対する剰余算結果” がややこしいなぁと感じたので、このページで情報をまとめておこうと思います。

特に除数が正の整数の場合に剰余演算も正の整数として得たい場合があると思いましたので、その際の対処法も記載しています。よろしければこちらも参考にしてください!

剰余演算結果の符号は “被除数” と同じになる

C言語において剰余演算は % 演算子によって実行することができ、被除数(割られる数)を除数(割る数)で割った時の余りを求めることができる演算になります。

剰余演算
被除数 % 除数

ややこしいのは、剰余演算の結果は “正の整数” としても “負の整数” としても表現できてしまうという点です。

C言語(特に C99 以降)においては、この剰余演算の結果を “正の整数” とするか、”負の整数” とするかが、除数と被除数それぞれの符号(正負)の関係によって下記のように決まります。

  • 正の整数 % 正の整数正の整数
  • 正の整数 % 負の整数正の整数
  • 負の整数 % 正の整数負の整数
  • 負の整数 % 負の整数負の整数

つまり、剰余演算結果の符号は “被除数” の符号と一致することになります。

具体的な数値として考えると下記のような結果になることになります。

  • 17 % 4 = 1
  • 17 % -4 = 1
  • -17 % 4 = -1
  • -17 % - 4 = -1

このような計算結果になることは事実なのですが、その一方で、特に 負の整数 % 正の整数 の結果は “正の整数” になって欲しいケースがあると思います(先ほどの例でいうと、-17 % 4 の結果は -1 ではなく 3 になって欲しい)。

例えば、剰余演算を “除数未満の整数に丸める” ことを目的として使用するケースなどがコレに当てはまると思います。

もっと具体的にいうと、リングバッファとして扱う配列の添字を剰余演算で計算する場合などです。配列の添字は “正の整数” である必要がありますが、剰余演算時に被除数が負の整数になってしまうと剰余演算結果も負の整数になり、さらにそれが配列の添字として扱われると配列外にアクセスすることになり不正アクセスとなってしまいます。

リングバッファの要素にmod演算を用いてアクセスする様子

上記のようなケースにも対応できるように、ここからは 負の整数 % 正の整数 の結果を “正の整数” として得たい場合の対処法を解説していきたいと思います。

負の整数 % 正の整数 の結果を “正の整数” にしたい場合の対処法

負の整数 % 正の整数 の結果が “負の整数” になるのは事実なので、これはもう変えられないです。

なので、この事実は受け止めて、下記のどちらかの方針で剰余算結果を正の整数として得るようにするのが良いと思います。

  • 被除数を正の整数にしてから剰余演算を行う
  • 剰余演算結果で得られた負の整数を正の整数にする

スポンサーリンク

被除数を正の整数にしてから剰余演算を行う

剰余演算では、被除数に除数の倍数を足したとしても結果が変化しないという特徴があります。例えば被除数の整数を x、除数の整数を y、さらに n を任意の整数とすると下記が成立します。

剰余算の等式
x % y = (x + n * y) % y

ですので、被除数が負の整数の場合は、被除数が正の整数になるまで除数の倍数を足してやれば、負の整数 % 正の整数 の結果を 正の整数 % 正の整数 により得ることができるようになります。

そして、前述の通り 正の整数 % 正の整数 の結果は “正の整数” ですので、結果的に 負の整数 % 正の整数 の結果を “正の整数” として得られることになります。

具体的に、”被除数が負の整数” “除数が正の整数” の場合に剰余演算結果を “正の整数” として得るための関数例が下記のようになります。

剰余演算結果を正の値にする1
#include <stdio.h>
#include <stdlib.h>

int positive_mod(int x, int y) {
    int divisor = y; /* 除数 */
    int dividend = x; /* 被除数 */

    while (dividend < 0) {
        /* 被除数が正の値になるまで除数を足し算 */
        dividend += divisor;
    }

    return dividend % divisor;
}

int main(int argc, char *argv[]) {

    int dividend;
    int divisor;
    int remainder;

    if (argc != 3) {
        printf("引数に被除数と除数を指定してください\n");
        return -1;
    }

    dividend = atoi(argv[1]);
    divisor = atoi(argv[2]);
    remainder = positive_mod(dividend, divisor);

    printf("%d %% %d = %d\n", dividend, divisor, remainder);

    return 0;
}

positive_mod 関数が、”被除数が負の整数” “除数が正の整数” の場合に剰余演算結果を “正の整数” として算出する関数になります。

ただし、対応しているのはあくまでも “除数が正の整数” の場合のみです。除数が “負の整数” の場合はうまく動作しないので注意してください。

剰余演算結果で得られた負の整数を正の整数にする

剰余演算で得られる結果は、正の整数であっても負の整数であっても絶対値は必ず “除数未満” となります。

ですので、剰余演算結果が負の整数である場合は、その結果に “除数” を一回足してやれば、剰余演算を結果を正の整数として得ることができます。

この方針で剰余演算を算出する場合、先程示した positive_mod 関数は下記のようになります。

剰余演算結果を正の値にする2
#include <stdio.h>
#include <stdlib.h>

int positive_mod(int x, int y) {
    int divisor = y; /* 除数 */
    int dividend = x; /* 被除数 */
    int remainder;

    remainder = dividend % divisor;

    if (remainder < 0) {
        /* 除数を足して正の整数にする */
        remainder += divisor;
    }

    return remainder;
}

int main(int argc, char *argv[]) {

    int dividend;
    int divisor;
    int remainder;

    if (argc != 3) {
        printf("引数に被除数と除数を指定してください\n");
        return -1;
    }

    dividend = atoi(argv[1]);
    divisor = atoi(argv[2]);
    remainder = positive_mod(dividend, divisor);

    printf("%d %% %d = %d\n", dividend, divisor, remainder);

    return 0;
}

ただし、こちらも対応しているのはあくまでも “除数が正の整数” の場合のみなので注意してください。

まとめ

このページでは負の値に対する剰余演算の動作についてまとめました。さらに、剰余演算の被除数が負の値である時に、剰余演算結果を正の値にするための対処法についても解説しました。

基本的に剰余演算結果の符号は、被除数と同じ符号になります。つまり、剰余演算結果が “正の値” or  “負の値” のどちらになるかは被除数によって決まります。

  • 正の整数 % 正の整数正の整数
  • 正の整数 % 負の整数正の整数
  • 負の整数 % 正の整数負の整数
  • 負の整数 % 負の整数負の整数

ただし、特に 負の整数 % 正の整数 の演算結果としては、負の整数ではなく “正の整数” のものを得たいという方も多いのではないかと思います。その際は下記の方針で剰余演算結果を正の整数として得らようにしましょう!

  • 被除数を正の整数にしてから剰余演算を行う
  • 剰余演算結果で得られた負の整数を正の整数にする

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です