最近、同じような投稿ばかりでワンパターンになってきました。そこで、少し趣向を変えて違う話題を取り上げてみることにします。
先日職場で話題になった非数についてです。せっかくなので備忘録代わりに投稿しておくことにします。すでにご存じの方にとっては面白みのない内容ですのでスルーしてください。
「非数」というのはその名の通り「数ではない」という意味です。NaNという呼び方の方がなじみがあるかもしれませんね。NaNはNot a numberの意味です。
非数はいろんなプログラミング言語で扱うことができます。非数を扱うための関数などは、(大文字小文字や区切り文字の違いなど)微妙に名前が違いますが、大体似たような感じになっているはずです。
C言語の場合、非数を作るにはnan関数を使います。nan関数はdouble型用ですので、float型の非数を作るにはnanf関数を、long double型の非数を作るにはnanl関数を使います。
nan関数は<math.h>で宣言される関数で、次のような形式になっています。
0 1 2 |
double nan(const char* tagp); |
実は非数には種類があって、qNaN(quiet NaN)とかsNaN(signaling NaN)とかがあります。FPUの仕様によってどんな種類の非数が使えるかは変わってくるのですが、詳しくはここでは触れません。
C言語の標準規格でもtagpに指定する具体的な文字列は規定されていません。tagpが指す文字列を構成する文字は英数字と下線のみが使えるようです。普通は空文字列を使ってnan(“”)と書くのだと思います。
明示的にnan関数を使う場合以外では、0.0/0.0の結果やsqrt(-1.0)の結果が非数になるようです。
さて、ここからが本題です。
この非数ですが、浮動小数点数の無効値を表現するにはちょうどいい存在です。確かD言語では浮動小数点数の初期値はNaNだったと思います。C言語にはNallableやoptionalがありませんので、単独のdouble型などで無効値を表現できるのであれば、それに越したことはありません。double型の変数が有効かどうかは、その値がNaNかどうかを調べればいいのです。
ところが、次のようなコードは驚くべき結果になります。
0 1 2 3 4 |
double x = nan(""); if (x == nan("")) puts("xは非数"); |
普通に考えると「xは非数」と出力されそうなものです。ところがそうはならず、結果は何も出力されません。さらに、次のようなコードも試していましょう。
0 1 2 3 4 |
double x = nan(""); if (x != nan("")) puts("xは非数ではない"); |
今度は「xは非数ではない」と出力されます。xは間違いなくnan(“”)で初期化していますし、その後更新されていないのにです。
このように、非数には奇妙な特性があります。ある変数が非数かどうかを調べるには、等価演算子を使うのではなく、isnan関数を使います。isnan関数も<math.h>で宣言されています。float型ではisnanf関数を、long double型ではisnanl関数を使うようにしてください。
0 1 2 3 4 |
double x = nan(""); if (isnan(x)) puts("xは非数"); |
今度は期待通りに「xは非数」と出力されました。
今回はC言語の例を取り上げましたが、ほかの言語でも大体事情は同じはずです。先日職場で話題になったときも、プログラミング言語はC#でした。C#ではDouble.IsNaNメソッドを使う必要があります。
こんな感じで浮動小数点数の無効値を表すのに非数は便利ですが、扱い方をわかっていないと思ったように動いてくれません。知っているかどうかだけで難しいことではありませんので、機会があればぜひ非数を有効活用してみてください。