RealLib のソースコード読みを始めるはずだったんですが、なんだか全然進んでないので適当なまとめエントリでお茶を濁します！ RealLib が普通にかっこよすぎるので紹介しまくりたくなりましたので紹介記事です。





実数計算と誤差

たいていのプログラミング言語の「実数 = 浮動小数点数」の計算には「誤差」があります。たとえばPythonのばあい：

Python 2.5 (r25: 51908 , Sep 19 2006 , 09: 52 : 17 ) [MSC v. 1310 32 bit (Intel)] on win32 Type "help" , "copyright" , "credits" or "license" for more information. >>> 0.1 + 0.1 + 0.1 - 0.3 5.5511151231257827e-017

0.1 を 3 回足しても 0.3 にはなりません。少しだけずれちゃってます。

この例のばあい、誤差の原因は、「浮動小数点数」が2進数で表現されていることです。"0.1" は2進数で書くと無限小数なので、途中で打ち切って記憶されてしまい、正確な "0.1" とは少しずれた値になってしまうのです。

それでは困る場合のために、多くのプログラミング言語には、10進数で実数を記憶する「BigDecimal」のようなライブラリが用意されています。またまた Python のばあい：

>>> Decimal( "0.1" ) + Decimal( "0.1" ) + Decimal( "0.1" ) - Decimal( "0.3" ) Decimal( "0.0" )

ピッタリ "0.0" になりました！

では、Decimal のようなライブラリを使えば、実数計算の誤差は完全に消えてなくなるのでしょうか？ 答えは否。

>>> Decimal( 8 ) / Decimal( 7 ) * Decimal( 7 ) Decimal( "8.000000000000000000000000001" )

"8/7" は10進数では無限小数になってしまうので、Decimalでも途中で打ち切られてしまいます。その結果、7倍しても元の8には戻らず、やっぱり微妙にずれます。Pythonのデフォルトでは途中の打ち切りは28ケタですが、これを調整してどれだけ長めにとっても、

>>> getcontext().prec = 80 >>> Decimal( 8 ) / Decimal( 7 ) * Decimal( 7 ) Decimal( "7.999999999999999999999999999999999999999999999999999999999999999999999 9999999997" ) >>> getcontext().prec = 300 >>> Decimal( 8 ) / Decimal( 7 ) * Decimal( 7 ) Decimal( "8.000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000002" )

ずれるものはずれます。「7進数で表現するライブラリを導入すれば・・・！！」と思わず考えてしまいますが、何進数で表現しようとも、有理数で近似している限りは、たとえば sqrt や sin のような無理数を返す計算を入れたとたんに、ちょっとずれてしまうことは避けられません。（巧く実装しない限り）計算を繰り返すたびにこの誤差は少しずつ積もっていきます。