Java言語仕様 4.2.3 「浮動小数点数型とその形式、値」には以下のように規定されている。
NaNは順序付けされない(unordered)ため、数値比較演算子<、<=、>、>=は、オペランドのいずれかまたは両方がNaNである場合にfalseを返す。等値演算子==はいずれかのオペランドがNaNである場合にfalseを返し、非等値演算子!=はいずれかのオペランドがNaNである場合にtrueを返す。
NaNが順序付けされないというこの性質は、しばしばプログラマの想定外であるため、NaNとの直接の比較は行うべきでない。NaNのセマンティクスを考慮することなく浮動小数点値の比較を行うコードを書くと、問題が発生する可能性がある。たとえば、入力にNaN値が含まれることを考慮しないような入力値検査は予期せぬ結果をもたらす可能性がある。詳細は「NUM08-J. 浮動小数点数入力が例外値でないかを検査する」を参照。
違反コード
以下の違反コードではNaNとの直接の比較を行っている。NaNのセマンティクスに従い、NaNとの比較はすべてfalseとなる(!=オペレータだけは例外的にtrueを返す)。結果的に、この比較は常にfalseを返し、"result is NaN" メッセージが出力されることはない。
public class NaNComparison { public static void main(String[] args) { double x = 0.0; double result = Math.cos(1/x); // 入力が infinity のとき NaN を返す if (result == Double.NaN) { // 比較は常に false! System.out.println("result is NaN"); } } }
適合コード
以下のコードでは、Double.isNaN()メソッドを使用し、式がNaN値であるかどうかをチェックしている。
public class NaNComparison { public static void main(String[] args) { double x = 0.0; double result = Math.cos(1/x); // 入力が infinity のとき NaN を返す if (Double.isNaN(result)) { System.out.println("result is NaN"); } } }
リスク評価
NaN 値との比較は予期せぬ結果をもたらす可能性がある。
ルール | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
NUM07-J | 低 | 中 | 中 | P4 | L3 |
自動検出
NaNとの比較を検出することは容易である。NaN 値が含まれている可能性まで考慮して計算結果が正しく処理されたかどうかを判断するのは一般に困難である。ヒューリスティックな検査は役に立つかもしれない。
FindBugsはNaNとの比較を検知できるケースもある。
参考文献
[FindBugs 2008] | FE: Doomed test for equality to NaN |
[JLS 2005] | §4.2.3, "Floating-Point Types, Formats, and Values" |
翻訳元
これは以下のページを翻訳したものです。
NUM07-J. Do not attempt comparisons with NaN (revision 62)