普通のプログラマなら直感的、反射神経的に理解しているような、
初歩的で単純な小ネタを記事にしていこうかと思います。
初歩的が故に、私のようなヘボなプログラマはよくやらかします。
誤りや適切でない内容がある場合は、コメントいただければ、
修正、内容の調整等検討します。
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さんのコメントは、本記事に反映させていただきました。