計算環境の精度を当てる ― Intel CPU と Excel への応用 ―
小清水 (@curekoshimizu) です。
計算ツールに対して
その 計算精度 がわかると便利ではないでしょうか?
例えば、コンピュータにのっている
その Excel はどのくらいの精度 で計算してくれる?
こうしたアプリケーションだけでなく、
そのコンピュータの中に入っている、
Intel の CPU はどのくらいの精度 で計算してくれる?
こういうことがわかると 非常に便利・ワクワク します!
特に CPU にはいろんな精度で計算できる命令が揃っており、
- 単精度
- 倍精度
- 拡張倍精度
の命令が載っています。
この記事を読むと、
実感をもって これらの精度がわかってしまいます。
しかも 数学的背景 もあるので納得もできます!
Abstract
1. CPU 単精度・倍精度 の 計算精度を当てる
2. CPU 拡張倍精度 の計算精度を当てる
3. Excel の計算精度を当てる!?
こういう話のとき Excel の例を最初に書きそうなものです。
なぜだか Excel の例が 最後 になっていますが
それは 困った話 が見つかってしまったからです。
1. CPU 単精度・倍精度 の 計算精度を当てる
プログラミングを勉強すると必ず習う計算環境 である、
単精度・倍精度 の浮動小数点環境の計算精度をまとめてみましょう。
単精度の浮動小数点 (float・binary32) 環境は 2進24桁精度
倍精度の浮動小数点 (double・binary64) 環境は 2進53桁精度
と言われています。これを実証してみましょう
精度を確かめるプログラムの方針
((x + 1) - x) と 1 が同じ
となるのが、
普通の実数の世界 であります。
当たり前です。
しかしながら、
普通じゃないのが 浮動小数点数 であり、
この2つの式が成立しない場合があります。
Step1
の形で
から初めて大きくしていき
初めて成立しなくなる を見つけます
Step2
その の に対して
が成立しています。
この 1 を動かして
と続き、
初めて等号となってしまう 整数 を見つけます
つまり、
となる です。
この がわかると
進数計算環境であることがわかる
Step3
Step2 でわかった を使って
もう一度 Step1 に似たアルゴリズムを実施します。
の形で
から初めて大きくしていき
初めて成立しなくなる を見つけます
つまり となる
初めての が 精度 桁 を表す
という主張です。
補足的には、
2進数であることがわかっていればStep1とStep3 は等価になります。
このことについて
証明は次回の記事とさせていただき、計算例を見てみましょう
実際のプログラム
#include <stdio.h> void check_float_precision(void) { /* Step. 1 */ float xp; for (xp = 1.0f; (xp + 1.0f) - xp == 1.0f; xp *= 2.0f) ; /* Step. 2 */ float beta; for (beta = 1.0f ; (xp + beta) - xp != beta; beta += 1.0f) ; printf("base : %d\n", (int)beta); /* Step. 3 */ int precision = 0; for (xp = 1.0f; (xp + 1.0f) - xp == 1.0f; xp *= beta) { precision++; } printf("precision : %d\n", precision); } void check_double_precision(void) { /* Step. 1 */ double xp; for (xp = 1.0; (xp + 1.0) - xp == 1.0f; xp *= 2.0) ; /* Step. 2 */ double beta; for (beta = 1.0 ; (xp + beta) - xp != beta; beta += 1.0) ; printf("base : %d\n", (int)beta); /* Step. 3 */ int precision = 0; for (xp = 1.0; (xp + 1.0) - xp == 1.0; xp *= beta) { precision++; } printf("precision : %d\n", precision); } int main(void) { check_float_precision(); check_double_precision(); return 0; }
実行結果
base : 2 precision : 24 base : 2 precision : 53
と先程紹介したとおり、
2進24桁・2進53桁精度 の 「2・24・2・53」が出力されております。
さらに次の例をみてみましょう。
2. CPU 拡張倍精度 の計算精度を当てる
CPU は過去互換性を大事にしており、
Intel x86_64 CPU には すごく昔によく用いられていた、
x87 命令といわれている、
拡張倍精度命令 というものをもっております。
拡張倍精度環境は 2進64桁精度 と言われています。
こうした 古めかしい命令 もわかってしまうのでしょうか?
gcc を使ったコンパイルで -mfpmath=387 オプションをつけると、
強制してこの x87命令 で計算することができます。
$ gcc -mfpmath=387 precision_check.c -o precision_check $ ./precision_check
実行結果
base : 2 precision : 64 base : 2 precision : 64
float・double と宣言したプログラムであっても、
それが 無視されて 先程紹介したとおり、
2進64桁精度 の 「64」が出力されております。
3. Excel の計算精度を当てる!?
B列に を計算し、
その結果を使い を計算した表を作成します。
この表を使えば
となり、
このことから
Excelは 2進50桁 の計算環境 であることがわかります。
と思いますよね?
2進50桁ってなんだ?ということになるわけです。
なぜならあまり聞き慣れない精度のためです。
よく使われる精度は上でこれまででてきた
- 2進24桁 (単精度)
- 2進53桁 (倍精度)
- 2進64桁 (拡張倍精度)
であります。
ためしに、上の表を次のように変えてみます。
を計算するのではなく
を計算してみます。
そして、正しく計算されたところは 0 になります。
もっと下まで計算しますと
となり、
同じく2進数であることは確かめられるのですが、
Excelは 2進53桁 の計算環境 になりました。
何がいいたい?
ある方法だと Excel は「2進50桁」
ある方法だと Excel は 「2進53桁」(倍精度) という結果になりました。
どうしてこうなったのかわからないため、
Excel詳しい方教えてください!
ちなみにですが CPU の倍精度環境で 同じ計算をすると、
の表と同じ結果を得ますので、
Excel はおそらく 倍精度精度で計算できるのだと思いますが…。
ということはアルゴリズムがおかしい?
よく知られたはずの方法なのですが
端的にいうと Excel の次のおかしな結果のせいで求められないことがわかります:
まとめ
CPU を例に 浮動小数点環境の精度をあてるプログラムを提示できました。
残念ながら Excel の場合はよくわからない結果になってしまった…。
(Excel が変な挙動を示す新しい例?)
記事が長くなってしまったため、
次回の記事はこのアルゴリズムの証明を紹介予定です。
浮動小数点数については下で詳しくまとめていますので
こちらも是非御覧ください!
(追記)
本記事の証明記事書きました!