MyEnigma

とある自律移動システムエンジニアのブログです。#Robotics #Programing #C++ #Python #MATLAB #Vim #Mathematics #Book #Movie #Traveling #Mac #iPhone

32. 浮動小数点は実数ではない Chuck Allison:『プログラマが知るべき97のこと』

以下の記事は,オライリージャパン社から出版された

『プログラマが知るべき97のこと』

の中から1つのエッセイを選び,

そのエッセイを,クリエイティブコモンズ3.0の条件の元で転載したものです.

Creative Commons ― 表示 3.0 アメリカ合衆国 ― CC BY 3.0

本書の内容は,オープンソースモデルに従い,

ほぼ無制限で利用が可能です.

クリエイティブ・コモンズ表示3.0の条件下で,

自由に使用することができるのです.

つまり,どのエッセイも,

著者の名前を明記すれば,自由に転載,改変が可能であるということです.

ーー"はじめに"から抜粋 pXII


もし,他のエッセイを読みたい場合には,

記事末のリンクを辿るか,

以下のリンク先のTwitterアカウントのつぶやきからお探し下さい.

Twitter Account: 97 Things Bot



また,元の英文によるエッセイを読みたい方は,

こちらを参照して下さい.

Contributions Appearing in the Book - Programmer 97-things


32. 浮動小数点は実数ではない チャック・アリソン (Chuck Allison):『プログラマが知るべき97のこと』

浮動小数点数は,数学で言う所の「実数」とは違います.

PascalやFortranなど,

プログラミング言語によっては,

「実数」という名前で呼ばれることもありますが,

厳密には実数ではないのです.

実数は,まず精度が無限です.

完全に連続していて,

「桁落ち」もありません.

一方,浮動小数点数の精度は有限です.

実数というよりは,

むしろ整数に近いといっていいでしょう.

いわば

「非常に奇妙な整数」です.

「非常に奇妙」というのは,

数と数の間隔が,

通常の整数とは違って均等ではないからです.



たとえば「2147483647」という数

(符号付き32bit整数の最大値)

を32bitの浮動小数点型変数(仮にxとします)

に代入し,

xの値を出力したとします.

すると,その結果は,

「2147483648」

になります.

x-64を出力しても,

「2147483648」

になり,

x-65を出力すると,

結果はなんと,

「2147483520」

になってしまいます.

何故でしょうか?

それは,この範囲では,数の間隔が「128」になっていて,

浮動小数点の演算では,

最も近い値への「丸め」が行われているからです.



IEEEの規格で定められた浮動小数点数は,

基数を2とした科学的記数法で表現されます.

この記数法では,

たとえば「1.d1d2….dp-1×exp(2)」というふうに記されます.

pは仮数部の行玄に使われるビット数(単精度では24,倍精度では53)です.

2つの隣りあう数の間隔は,2^1-p+eとなります.

これはほぼε|x|と考えても大きな間違いはないでしょう.

εはマシンイプシロン(2^1-p)を表します.



浮動小数点数において,

数と数の間隔がどのようになっているのかを正しく把握しておけば,

「演算結果が想定と大きく異なってしまう」

という起こりがちな間違いを防ぐことができます.

例えば,方程式の根の探索などのような反復計算の際,

定められた以上の精度をもとめるようなコードを書いても意味はありません.

数と数の間隔が広すぎる場合,

永久に求める答えは得られず,無限ループに陥ってしまいます.



浮動小数点数は,実数の近似値です.

つまり,誤差が生じることは避けられないということです.

「丸め」によって生じる誤差が原因で,

演算結果が予測とまったく違って驚くことは珍しくないのです.

例えば,殆ど同じ大きさの数値どおしの減算をしたとします.

その場合,最上位の桁は,互いに相殺し合い,

最下位の桁にあった数(丸めによる誤差を含んだ数)

が最上位の位にまで繰り上がる,ということが起きます.

この演算結果を踏まえて更に演算を重ねていくと,

誤差はどんどん大きくなっていってしまいます.

こういう悲惨な結果を防ぐためには,

自分の書いたコードのアルゴリズムをよく確認する必要があります.

例を一つ挙げておきましょう.

方程式「x^2-10000x+1=0」を二次元方程式の解の公式を使って解くとします.

この場合,式「-b+sqrt(b^2-4)」のオペランドの大きさはいずれもほぼ同じなので,

代わりに,根「r1=-b-sqrt(b^2-4)」を計算して,

その後にr2=1/r1を求めることができます.

二次方程式「ax^2+bx+c=0」の場合,根は必ず「r1r2=c/a」を満たします.



誤差が大きくなっていくという現象は,

もっと目立たないかたちで起きることもあります.

仮にe^xを単純に「1+x+x^2/2+x^3/3!+・・・」

という式で計算するライブラリがあったとします.

xが正の数であれば,この方法でも問題は起きないのですが,

もしxが絶対値の大きい負の値であったらどうなるのかを考えてみましょう.

その場合は,偶数番目の項の演算結果は大きな正の数になり,

そこから,奇数番目の項の演算結果を差し引くことになります.

それだけでは特に誤差が生じるわけではありません.

ただ問題は,偶数番目の項の演算結果に対する丸めです.

演算結果が大きな数であれば,かなり上位の桁で丸めが行われてしまうのです.

このことから,最終的な演算結果は,正の無限大に向かって大きくなり,

本来得られるべき結果とは大きく異なってしまいます.

この問題を解決する方法は簡単です.

xが負の数の場合は,ex=1/e^|x|を計算するようにすればいいのです.



最後に,金融関係のアプリケーションには

浮動小数点数を使うべきでないということも書いておくべきでしょう.

PythonやC#に,Decimalクラスが用意されているのはそのためです.

浮動小数点数は,元々,科学技術計算を効率的に行うことを目的にしたものです.

しかし,正確さを書いていては,効率がいくら良くても価値はありません.

浮動小数点数を使う時は,どういう時に丸め誤差が出るのかをよく知り,

その知識を踏まえた上でコーディングすべきです.


■著者データ

[チャック・アリソン (Chuck Allison)]

現在はユタヴァレー大学コンピュータ科学准教授だが,

それ以前は米国西部にてソフトウェアエンジニアとして約20年働いた.

かつてはC++98のアクティブコントリビュータであり,

「C/C++ Users Journal」の編集長も務めた他,

Bruce Eckelとともに著書『Thinking in C++, Volume 2』

の執筆もした.

C++ Sourceの創始者であり,

「Better Software Magazine」の寄稿編集者でもあった.

詳しくはサイトを参照のこと.

Chuck Allison Home




関連記事

1.分別のある行動 Seb Rose:『プログラマが知るべき97のこと』 - MY ENIGMA

2.関数プログラミングを学ぶことの重要性 Edward Garson:『プログラマが知るべき97のこと』 - MY ENIGMA

3.ユーザが何をするかを観察する (あなたはユーザではない) Giles Colborne:『プログラマが知るべき97のこと』 - MY ENIGMA

4.コーディング規約を自動化する Filip van Laenen:『プログラマが知るべき97のこと』 - MY ENIGMA

5.美はシンプルさに宿る Jorn Olmheim:『プログラマが知るべき97のこと』 - MY ENIGMA

6.リファクタリングの際に注意すべきこと Rajith Attapattu:『プログラマが知るべき97のこと』 - MY ENIGMA

7.共有は慎重に Udi Dahan:『プログラマが知るべき97のこと』 - MY ENIGMA

8. ボーイスカウト・ルール Robert C. Martin:『プログラマが知るべき97のこと』 - MY ENIGMA

9. 他人よりまず自分を疑う Allan Kelly:『プログラマが知るべき97のこと』 - MY ENIGMA

10. ツールの選択は慎重に Giovanni Asproni:『プログラマが知るべき97のこと』 - MY ENIGMA

11. ドメインの言葉を使ったコード Dan North:『プログラマが知るべき97のこと』 - MY ENIGMA

12. コードは設計である Ryan Brush:『プログラマが知るべき97のこと』 - MY ENIGMA

13. コードレイアウトの重要性 Steve Freeman:『プログラマが知るべき97のこと』 - MY ENIGMA

14. コードレビュー Mattias Karlsson:『プログラマが知るべき97のこと』 - MY ENIGMA

13. コードレイアウトの重要性 Steve Freeman:『プログラマが知るべき97のこと』 - MY ENIGMA

14. コードレビュー Mattias Karlsson:『プログラマが知るべき97のこと』 - MY ENIGMA

15. コードの論理的検証 Yechiel Kimchi:『プログラマが知るべき97のこと』 - MY ENIGMA

16. コメントについてのコメント Cal Evans:『プログラマが知るべき97のこと』 - MY ENIGMA

17. コードに書けないことのみをコメントにする Kevlin Henney:『プログラマが知るべき97のこと』 - MY ENIGMA

18. 学び続ける姿勢 Clint Shank:『プログラマが知るべき97のこと』 - MY ENIGMA

19. 誰にとっての利便性か Gregor Hohpe:『プログラマが知るべき97のこと』 - MY ENIGMA

20. すばやくデプロイ,こまめにデプロイ Steve Berczuk:『プログラマが知るべき97のこと』 - MY ENIGMA

21. 技術的例外とビジネス例外を明確に区別する Dan Bergh Johnsson:『プログラマが知るべき97のこと』 - MY ENIGMA

22. 一万時間の訓練 Jon Jagger:『プログラマが知るべき97のこと』 - MY ENIGMA

23. ドメイン特化言語 Michael Hunger:『プログラマが知るべき97のこと』 - MY ENIGMA

24. 変更を恐れない Mike Lewis:『プログラマが知るべき97のこと』 - MY ENIGMA

25. 見られて恥ずかしいデータは使わないこと Rod Begbie:『プログラマが知るべき97のこと』 - MY ENIGMA

26. 言語だけでなく文化も学ぶ Anders Noras:『プログラマが知るべき97のこと』 - MY ENIGMA

27. 死ぬはずのプログラムを無理に生かしておいてはいけない Verity Stob:『プログラマが知るべき97のこと』 - MY ENIGMA

28. 「魔法」に頼りすぎてはいけない Alan Griffiths:『プログラマが知るべき97のこと』 - MY ENIGMA

29. DRY原則 Steve Smith:『プログラマが知るべき97のこと』 - MY ENIGMA

30. そのコードに触れてはならない! Cal Evans:『プログラマが知るべき97のこと』 - MY ENIGMA

31. 状態だけでなく「ふるまい」もカプセル化する Einar Landre:『プログラマが知るべき97のこと』 - MY ENIGMA