std::valarray と NaN と max(), min(), sum()

標準C++ライブラリの std::valarray は、浮動小数点数型で NaN を含んでいると、メンバ関数 max(), min(), sum() の値が NaN になったりする。これは無論 valarray に限ったことではなく、std::vector だろうと配列だろうと、標準テンプレートライブラリ関数 std::max_element, std::min_element, std::accumulate を使えば同じく NaN になる。なお、C++の規格では NaN は必須ではないのでこの挙動は実装依存だが。しかし、NaN ではない要素の最大値、最小値、総和くらい取りたいではないか! 実は valarray ではそれが簡単にできる。

// NaN 以外の総和。ここで v は std::valarray<double> 型。
std::valarray<double>(v[v == v]).sum();
// NaN を含まない要素数
std::valarray<double>(v[v == v]).size();
// NaN の要素数
std::valarray<double>(v[v != v]).size();

ちなみに、NaN の置き換えもできる。

v[v != v] = 0.0;  // NaN を 0 で置き換え。

NaN でなくても、比較演算子次第で任意の数値に対してこのような処理が可能。こういう簡潔さも valarray の魅力のひとつだけど…ただでさえマイナーな valarray の仕様を知らない人には伝わらなそう…なんて不憫な valarray たん…

GCCSTL実装だと、開始イテレータが指す値がNaNでなければmax、min、std::max_element、std::min_elementは正しい値を返してくれる。