abs関数の落とし穴(最小の負数を引数にした場合の未定義の動作問題)

C言語

abs関数には未定義の動作がある

C言語には絶対値を返す標準ライブラリ関数absがあります。
C99では、

#include <stdlib.h>

int abs(int j);


と定義されており、引数jの絶対値を戻り値で返します。
ここで注意しなければいけないのは、引数と戻り値の型が同じint型であることです。
つまり、「int型である引数jは、絶対値がint型で表現できる範囲でなければいけない」、ということです。
この範囲を超えた時の動作は、未定義となっています。

int型の表現範囲

int型の表現範囲ですが、int型が4byteの処理系の場合、

-2,147,483,648 ~ 2,147,483,647

となります。
しかし、よく見ると、-2,147,483,648の絶対値はint型で表現できないことがわかります。(最小の負数の絶対値は、2の補数では表現できない)
つまり、「最小の負数を引数としたabs関数の処理結果(戻り値)は未定義である」という訳です。

実際の処理例

規約で未定義の処理は、「予測不可能な結果を返しても良い」ことになっており、処理系に依存します。
参考までに、ある処理系で実行した結果は以下のとおりです。

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int x1 = -2147483647;
    int x2 = -2147483648;

    x1 = abs( x1 );
    x2 = abs( x2 );
    
    printf("x1 = %d\n"  , x1);
    printf("x2 = %d\n"  , x2);
}
x1 = 2147483647
x2 = -2147483648

x1は絶対値を返していますが、x2は絶対値を返していません。(この処理系では、そのままの値を返すようです。詳しくは、処理系のコンパイラの仕様を確認する必要があります。)

まとめ

abs関数では、最小の負数を引数として渡してはいけません。
最小の負数となる恐れがある場合、

  • 引数を事前にチェックする
  • 未定義時の処理を実装した自作関数を用意する

等の対応が必要となります。

補足

以下の整数型に対する絶対値を取得する標準ライブラリ関数についても、上記と同様な問題があります。

  • long int labs( long int j );
  • long long int llabs( long long int j );

コメント

タイトルとURLをコピーしました