作者: dune
日時: 2005/10/11(00:42)
名無しです。

最初に質問を読んだとき、この問題に対する答え(解法)は固定小
数点しかなく、自分で小数点位置を意識してコーディングするか、
固定小数点クラスを導入すべきだと思いました。なので bignum を
使う話が出たとき、それは浮動小数点の精度を上げているだけで、
0.1 ≠ 1/10 なんじゃないかと思ったのです。

ところが、

>有効桁数の指定については bignum であれば、a で指定します。

という説明を読んで「そんなオプションがあるのか」とモジュール
の説明を読んだところ、bignum が内部で使っている Math::BigInt
の説明には、

>The actual numbers are stored as unsigned big integers

という記述がありました。固定小数点ってことですね。

bignum は a(accuracy:確度)で有効桁数を、p(precision:精度
)で小数点位置を指定するようです。

今回の問題では 1053.59 がいちばん細かい値ですから、小数点以下
2桁まで正しく扱うという意味で p に -2 を指定すれば良いでしょ
う。a のデフォルトは 40 ということなので、これで整数部が 38 
桁、小数部が 2 桁の範囲の数値であれば浮動小数点の量子化誤差な
で計算ができるはずです(ほんまかいな)。もちろん、計算の途中
で 1  を 3 で割るようなことをしていないかチェックは必要ですが
(1 を 4 や 10 で割るのは結果が小数点以下2桁の範囲に収まるの
で ok)。

ということで、先のスクリプトをなおすと:

[スクリプト]
use bignum p => -2;	# 小数点以下二桁

my $a	=  200.00;
my $b	= 1053.59;

my $c	= $a * $b;	#=> 21078
my $d	= int($c);	#=> 21n78

my $e	= $c - $d;

printf("a = %+24.15lf (%s)\n", $a, $a);
printf("b = %+24.15lf (%s)\n", $b, $b);
printf("c = %+24.15lf (%s)\n", $c, $c);
printf("d = %+24.15lf (%s)\n", $d, $d);
printf("e = %+24.15lf (%s)\n", $e, $e);

[実行結果]
D:$ perl gomi.pl
a =     +200.000000000000000 (200)
b =    +1053.589999999999900 (1053.59)
c =  +210718.000000000000000 (210718.00)
d =  +210718.000000000000000 (210718)
e =       +0.000000000000000 (0.00)

D:$

1053.59 が 1053.58999... と表示されたのは %+24.15lf という書
式指定のために bignum オブジェクトがただの浮動小数点数に変換
されたのが原因で、内部で保持している値は厳密に 1053.58 なのだ
ろうと推測します。

どうでしょ。間違ってたら誰か教えてください。
--