作者: 藤岡和夫
日時: 2005/10/08(16:35)
On Sat,  8 Oct 2005 14:16:21 +0900 (JST)
qwerty25jp@... (TM) さんwrote:

> 初めまして、最近良くPerlを使っていますが、数値計算結果をINT関数で
> 少数点以下を切り捨てると、マイナス1となる問題が起きています。

 int関数は小数点以下を切り捨てるので当然の結果ではあるのです。int関数を
使う必要はないということになります。Perlは適当に丸めて表示してくれるので、
そのまま$cを使えば、"ほぼ"期待している数値を表示してくれるのです。

 これはコンピュータ(2進法で数値を表現するために)にとっては奥が深い問題
で(C言語によるアルゴリズム辞典の「四捨五入」の項を参照するとわかりやすい)、
Perlクックブックでも、「財務計算や核ミサイルなど、きわめて繊細な問題を取
り扱うプログラマは、コンピュータに組み込まれているロジックに頼るのではな
く、自分で丸め演算用の関数を実装します(数値解析についての参考書を読むこ
とをお薦めします)。」とレシピ2.2「浮動小数点数を丸める」の最後に書かれて
います。

> どの様な計算が行われているか、確認するプログラムで調べたところ、
> 浮動小数点らしい計算が行われ、結果端数が出ているためでした。
> 
> $a 	= 	200;
> $b 	= 	1053.5900;
> 
> $c 	= 	$a * $b;
> 
> $d	=	int($c);
> 
> $e	=	$c	-	$d;
> 
> print " $c   :	$d      :	    $e	            \n";
> 
>     210718  :	210717  :	    0.999999999970896
>      
> 金額計算なのでマイナス1でも、許されない状況です。
> このような端数が出ない方法は、どの様な書き方をすれば、
> よろしいでしょうか。何方かお教え下さい。

 printfやsprintf関数について調べるとよいでしょう。10進整数を使うと有効
桁は9桁ぐらいしかありません。大きい桁の整数は浮動小数点のコンパクト形式
を使って表示させるのが便利だと思います。しかし、上記のような問題があるこ
とを、"きわめて繊細な問題を取り扱う場合は"意識して、自己責任で常にチェッ
クしながら使う必要があるでしょう。次のようなスクリプトを動かしてみると数
値の背景にはいろいろと問題があることがわかるでしょう。

$a = 200;
$b = 1053.5900;

$c = $a * $b;

$d = int($c);

$e = $c - $d;

$ec = sprintf "%f.", $c;
$ed = sprintf "%f.", $d;

$f = $ec - $ed;

print "\$c = ", $c,"\n";
printf "\$c = %.15f\n", $c;
printf "\$c = %f (固定小数点形式): %g (コンパクト形式)\n", $c, $c;
printf "\$d = %f (固定小数点形式): %g (コンパクト形式)\n", $d, $d;
print "\$e = ",$e,"\n";
printf "\$e = %.15f\n", $e;
printf "\$e = %f (固定小数点形式): %g (コンパクト形式)\n", $e, $e;
print "\$f = ", $f,"\n";
printf "\$f = %.15f\n", $f;
printf "\$f = %f (固定小数点形式): %g (コンパクト形式)\n", $f, $f;

藤岡 和夫
kazuf@...
TS Networkのために http://homepage1.nifty.com/kazuf/