58
46

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

破滅的プログラミングの末路#01 「整数の絶対値でやらかす」

Last updated at Posted at 2018-04-18

普通のプログラマなら直感的、反射神経的に理解しているような、
初歩的で単純な小ネタを記事にしていこうかと思います。
初歩的が故に、私のようなヘボなプログラマはよくやらかします。

誤りや適切でない内容がある場合は、コメントいただければ、
修正、内容の調整等検討します。

1. 整数の絶対値

多くのプログラミング言語では、整数の絶対値を返してくれるような、
関数なり、メソッドなりが用意されています。

たとえば、
int result = Math.abs(-15);とか実行すると、引数では負の整数を指定していますが、
resultには、符号が取れた15が代入されます。
これは、多くのプログラマにとって、どうということないことでしょう。

2. 整数型

C言語とかJavaとかC#とか多くのプログラミング言語には整数型というものがあります。
整数を取り扱うための型ですが、今回はそれ自体が本題ではありません。
C, Java, C#あたりの経験者がイメージしやすいint型を例に考えてみます。

int型のサイズは、言語によったり環境にもよると思いますが、
とりあえず、32ビットの符号付きの整数型を考えてみます。

32ビットの符号付の整数型が表現できる範囲は多くのプログラミング言語の場合は、
-2,147,483,648 から 2,147,483,647
になっているはずです。

これ見ただけでも既に怪しい雰囲気が漂ってます。

3. 32ビット符号付き整数型で一番小さな整数の絶対値を考えてみる

int result = Math.abs(-2147483648);とやると、resultはどうなるでしょう?
正の整数としては、2147483647までなので、よからぬことになりそうです。

ちなみに、C#のSystem.Mathでは、OverflowExceptionが投げられます。

try
{
    int result = System.Math.Abs(Int32.MinValue);
}
catch(OverflowException)
{
    Console.WriteLine("OverflowExceptionが発生");
}

Javaのjava.lang.Mathでは、絶対値と言えど変わらず、負の整数の-2147483648になります。
(これは仕様通りの動作です。)

int result = Math.abs(Integer.MIN_VALUE);

// 絶対値だけど、-2147483648のまんま。
System.out.printf("result = %d%n", result);

C/C++のcmathの場合、私の手元の環境では、負の整数の-2147483648になりました。
こちらはどうなるかは未定義なので、環境によっては、別の結果になるかもしれません。

#include <cmath>

int main()
{
  int result = abs(INT_MIN);

  return 0;
}

4. 末路

絶対値を返す関数、メソッドに、符号付き整数型の最小値を渡す可能性があるようなコードを書いていると、
"いずれ"よからぬことが発生する可能性があります。

"いずれ"というのがポイントで、今回の例では、問題があるのは、
-2,147,483,648 から 2,147,483,647までの、4,294,967,296個の整数値のうち、
1つだけになります(-2,147,483,648だけ)。
問題は、なかなか顕在化しない場合もあります。
よりサイズの小さな整数型だと、より問題は発生しやすくなるはずです。

5. 対策

設計上、符号付き整数型の最小値を渡す可能性がない場合は、問題ありません。
そうでない場合は、対策が必要です。

基本的には、符号付き整数型の最小値に対して特別な処理をすることになります。
例えば、最小値は除外されるように実装する、
最小値の絶対値も必要である場合は、より大きなサイズが扱える整数型を利用するなどになると思います。
(より大きなサイズの整数型でも最小値では問題は発生します。)

要件等によっては、BigIntegerのような任意精度の整数型の活用も考えられます。
たとえば、JavaやC#の場合は、BigIntegerクラスや構造体が用意されていて、
abs()メソッド(C#では、staticメソッド)もあります。
BigIntegerの場合は、オーバーフローする心配はありません。
(メモリが不足する場合はあるかもしれませんが、
メモリが不足するほど巨大な数値を扱う人が、もしいれば、敬意を表したいと思います。)

また、可能な限り、符号付き整数型の最小値も評価できるような単体テストを組みましょう。

6. 謝辞

コメントいただいた皆様ありがとうございました。

@saka1029さんのコメントに関連した内容は、いずれ、独立した記事にしようと思います。
@tsuyoshi_choさんのコメントは、本記事に反映させていただきました。

58
46
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
58
46

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?