目的
-
数値計算が可能か を判定する際に、
isnan()
の使用が妥当かを検証する- 検証に使う関数は、 isnan()以外に、
isinf()
とisfinite()
- 検証に使う関数は、 isnan()以外に、
結論
- 数値計算が可能かどうかを判定する場合は、
isfinite()
の使用が良い- isnan()の場合、無限大を検出できないため
対応表
- 各関数と各非数の対応関係を表にまとめた
- 太字 の箇所は、私の肌感覚と異なる判定(違和感を感じる箇所)
数値 | inf | nan | -nan(ind) | 備考 | |
---|---|---|---|---|---|
isnan | False | False | True | True | infを検出してくれない |
isinf | False | True | False | False | nanを検出してくれない |
isfinite | True | False | False | False | nan, inf どちらも検出可 |
isnan()を使ったコードで発生したバグ
- 実際の業務で発見したバグ
-
分野: 機械学習案件の数値計算
- ※説明のため、非常に単純化した例を示します。
-
以下のような足し算関数
add()
を定義する- プログラマーは、isnan()を設置していたので、不正な値が入っても安心できると考えていた
- しかし、この関数では、引数x,yに無限大が入ってしまった時に検出できない。
-
サンプルコードの例では、何かの拍子に
y = 無限大
が入ってしまったと仮定して、話を進めます。
-
bug.cpp
#include <iostream>
#include <string>
#include <cmath>
#include <limits>
#include "classA.h"
double add(double x, double y){
// x, yが数値計算可能か判定して欲しい...
if(std::isnan(x) || std::isnan(y)){
return 0;
}
return x + y;
}
int main(void){
double x = 1.0;
double y = INFINITY; // 何かの拍子に無限大が入る
std::cout << "add(x,y) = " << add(x,y) << std::endl;
return 0;
}
実行結果
add(x,y) = inf
-
引数にはfloatを入れてるし、isnan()で検出できるはずなのに...??
- このように、プログラマーの意図と反した値が帰ってきた。
-
実行〜バグ発見までの経緯
- 実際に走らせると、実行時によく分からない値が返却される
- コンパイルエラーや実行時エラーは、吐かれない。
- しかし、なぜかAIの予測結果がおかしい...
- 計算は何層にも渡っているため、1層ずつ値を確認...
- 発見まで非常に苦労した。
所感
- 機械学習など、絶対的な正解がなくデバッグが難しい分野で上記のような勘違いをすると、問題の発見が非常に難しくなります。
- 筆者は、
isfinite()
を積極的に使った方がよいと考えています。
(おまけ)検証プログラム
- 以下は、おまけです。
- 対応表を作るために作成したコードを乗せます。
test.cpp
#include <iostream>
#include <string>
#include <cmath>
#include <limits>
int main(void){
float num = 1;
float inf = INFINITY;
float nan = NAN;
float nan_ind = inf - inf;
// isnanの判定
std::cout << "isnan(num) = " << std::isnan(num) << "\t\t"
<< "isnan(inf) = " << std::isnan(inf) << "\t\t"
<< "isnan(nan) = " << std::isnan(nan) << "\t\t"
<< "isnan(nan_ind) = " << std::isnan(nan_ind) << std::endl;
// isinfの判定
std::cout << "isinf(num) = " << std::isinf(num) << "\t\t"
<< "isinf(inf) = " << std::isinf(inf) << "\t\t"
<< "isinf(nan) = " << std::isinf(nan) << "\t\t"
<< "isinf(nan_ind) = " << std::isinf(nan_ind) << std::endl;
// isfiniteの判定
std::cout << "isfinite(num) = " << std::isfinite(num) << "\t"
<< "isfinite(inf) = " << std::isfinite(inf) << "\t"
<< "isfinite(nan) = " << std::isfinite(nan) << "\t"
<< "isfinite(nan_ind) = " << std::isfinite(nan_ind) << std::endl;
return 0;
}
出力
a.out
isnan(num) = 0 isnan(inf) = 0 isnan(nan) = 1 isnan(nan_ind) = 1
isinf(num) = 0 isinf(inf) = 1 isinf(nan) = 0 isinf(nan_ind) = 0
isfinite(num) = 1 isfinite(inf) = 0 isfinite(nan) = 0 isfinite(nan_ind) = 0
- ※ 1 = True, 0 = False
処理系
- OS: Windows10 Pro
- CPU: Intel(R) Core(TM) i7-6700K
- コンパイラ: VS2017 標準コンパイラ