たまたま調べる機会があったのでメモ。
実施環境: Splunk Free 8.2.2
Splunk では、「数値」を扱うことがよくあります。
そして、その「数値」はしばしば非常に大きな値となります。
ですが実は、 Splunk で扱える「数値」の範囲には限界があります。
試しに、以下のSPLを実行してみます。
pow は乗算を表す関数で、ここでは2を何回か掛け算した値に1を足した値を計算しています。
| makeresults count=100
| streamstats count AS CNT
| eval NUM = pow(2, CNT) + 1
| eval CHK = if(NUM % 2 = 1, "T", "F")
| where CHK = "F"
| head 1
| table CNT, NUM
上記を見ると、2の53乗に1を足した値が「2で割って1余る値ではない」、すなわち「奇数ではない」ことになっています。
これは明らかにおかしいです。
2の53乗は「9,007,199,254,740,992」なので、この値の周辺の数値の下1桁の動きを見てみます。
| makeresults count=10
| streamstats count AS CNT
| eval NUM = 9007199254740987 + CNT
| table CNT, NUM
ちょうど「9,007,199,254,740,992」を境に、下1桁の動きがおかしくなっているのがわかります。
Splunk もプログラムの1つである以上、1つの数値が入る「箱」には当然限界があります。
この「箱」のサイズ上、2の53乗 =「9,007,199,254,740,992」(およそ9000兆、桁数なら16桁)が正確に整数を扱える範囲のようです。
マイナス方向も、結果は同様となります。
| makeresults count=10
| streamstats count AS CNT
| eval NUM = -9007199254740987 - CNT
| table CNT, NUM
逆に、非常に小さい値についても確認してみましょう。
試しに、以下のSPLを実行してみます。
先ほどとは逆に、1を何回か2で割り算した値に1を足した値を計算し、それが「1より大きい値か」=「1に丸められていないか」を確認します(ちなみに、これで求められる「1より大きい最小の値と1の差」を「計算機イプシロン」と呼ぶそうです)。
| makeresults count=100
| streamstats count AS CNT
| eval NUM = pow(2, CNT * -1) + 1
| eval CHK = if(NUM > 1, "T", "F")
| where CHK = "F"
| head 1
| table CNT, NUM
2の-53乗に1を足した値が「1に等しい」となっています。2の-53乗は0.00000000000000011...なので、小数点以下の桁数も16桁くらいが限界となります。
上記くらい大きい、または小さい値を扱うことはそうそうありませんし、あってもそのような細かい誤差まで気にすることは少ないと思います。
ただ、長期間の統計で合計値を計算してこのような大きい値が出てきたり、小さい値での誤差が計算を重ねるうちに無視できない丸め誤差を生じさせたりする可能性もあるので、注意が必要です。
ちなみに、この扱える値の範囲を考えると、どうやら Splunk における数値は「倍精度浮動小数点型」(標準規格「IEEE 754-2008」における「Binary64」)という形式を採用しているようです(きちんと確かめたわけではありませんが)。この形式は64ビットのデータ長を持ち、うち符号部は1ビット、指数部は11ビット、仮数部は52ビットとなります。なお、標準規格「IEEE 754-2008」に準拠しているなら、符号部/指数部/仮数部と数値の関係は以下のようになります(仮数部は小数であることに注意)。
(-1)^{符号部}×2^{指数部-1023}×(1+仮数部)
ところで、標準規格「IEEE 754-2008」における「Binary64」では、指数部のビットが全て1の場合は特別なパターンを表す値として予約されています。
そのため、指数部の最大値は$2^{11}-1-1=2046$となります。
仮数部は1未満の小数なので、$2^{2046-1023+1}=2^{1024}$以上の数値は「Binary64」では表現できないことになります。
では、そのような値について、 Splunk はどう表現するのでしょうか。
答えは以下の通りです。
| makeresults count=1
| eval NUM = pow(2,1024)
| table NUM
「Infinity」=「無限大」と出力されました。
まあ、ここまで大きな値を使用することはないと思いますが。