2009-06-03 10 views
2

私は頻度分布を計算し、浮動小数点の丸め誤差の問題に対処するためにPerlでStatistics::Descriptiveライブラリを使用しています。PerlのStatistics :: Descriptiveで無限ループを引き起こす丸め誤差を回避するにはどうしたらいいですか?

私は統計モジュールに0.205と0.205の2つの値(他の数値とsprintf'dから取得)を渡し、頻度分布を計算するよう依頼しますが、無限ループに陥っています。私はそれがやっていることがわかりますデバッガでステップスルー

:私は期待通り(範囲は最大 - 最小である)

my $interval = $self->{sample_range}/$partitions; 

my $iter = $self->{min}; 

while (($iter += $interval) < $self->{max}) { 

    $bins{$iter} = 0; 

    push @k, $iter; ##Keep the "keys" unstringified 

} 

$自己> sample_rangeは2.77555756156289e-17ではなく0を返します。これは、ループ((min + = range)< max)が(すべての目的と目的のために)無限ループに入ることを意味します。

DB < 8> print $ self - > {max};
0.205
DB < 9> print $ self - > {min};
0.205
DB < 10> print $ self - > {max} - $ self - > {min};
2.77555756156289e-17

これは丸めの問題のようです。私はこれを私の側で修正する方法を考えることはできません、そして、私はライブラリの編集が良いアイデアであるとは確信していません。私は回避策または代替の提案を探しています。

乾杯、 ニール

答えて

5

私はStatistics :: Descriptive maintainerです。数値的性質のため、多くの丸め問題が報告されています。私は、この特定のものが、最近リリースされたものを使用しているものに、+ =の代わりに除算を使って、後のバージョンで修正されたと考えています。

CPANのthe most up-to-date versionを使用してください。

+0

こんにちは、Shlomi!あなたはこの質問に気づきました。私にあなたにそれへのリンクを電子メールで送ることから私を救った。私は新しいバージョンがまだ$ bins {$ self-> max()} = 0のようなハッシュキーとして数字を使っているのを見ています。この丸めを回避するには、pack "F"(5.8.0+が必要)を使用し、キーを使用するときはいつでも解凍してください。 – ysth

+0

優秀、ありがとう!私は新しいバージョン、私の欠陥を確認しておくべきだった。私の最初のスタックオーバーフローの質問に対するこの応答に非常に感銘を受けました。返答したすべての人に再び感謝します。 – NeilInglis

3

ない正確に丸め問題。あなたは何か欠陥は、それがサンプルの範囲は$パーティション片に分割することができますを前提とモジュールでありますように私には見える

printf("%.18g %.18g", $self->{max}, $self->{min}); 

などで、より正確な値を見ることができます。浮動小数点は無限の精度を持たないため、これは必ずしも可能ではありません。あなたの場合、最小値と最大値は正確に隣接した表現可能な値なので、複数のパーティションは存在できません。私はモジュールがパーティションを使用していることを正確にはわからないので、この影響がどのようなものかはわかりません。 モジュールのもう1つの問題は、数値をハッシュキーとして使用していることです。 は暗黙のうちに値をわずかに丸めます。

あなたはモジュールに を供給する前にstringizationを通して、あなたのデータを洗濯中にいくつかの成功を持っていることがあります。これは、少なくとも(デフォルトの印刷精度と)同じ表示されていることを二つの数字が実際にあることを保証します

$data = 0+"$data"; 

を等しい。

+0

よろしくお願いいたします。 Maxは実際には0.20500000000000002、最小値は0.20499999999999999です。なぜそれが間違っているのかを説明しています。 いくつかの回避策を試してみます。 – NeilInglis

-1

無限ループを引き起こしてはいけません。そのループが無限になる原因は、$self->{sample_range}/$partitionsが0の場合です。

+0

ええ、どちらもそうは思わなかった DB <12> p $ iter; 0.205 DB <13> p $ interval; 3.46944695195361e-18 DB <14>のp $のITER + = $間隔 0.205 DB <15>のp $自己 - > {最大} 0.205 DB <16> P($のITER + = $間隔)< $self-> {最大} そう((0.205 + 3.46944695195361e-18)<0.205)は真であると評価される。 もちろん、それは長い一日だったので、私はボールから外れる可能性があります... – NeilInglis

+0

Hrmフォーマットが失敗します。ごめんなさい。 – NeilInglis

+0

Nope;たとえば数字1と1 + 2 ** - 52を取る。彼らは2 ** 52で違う。 4つのパーティションが必要であると仮定すると、2 ** - 54(これは明らかに非ゼロです)の間隔が与えられますが、それを1に追加しようとすると、(ほとんどのプラットフォームで)1を変更しないままにします。 1 + 2 ** - 54までの値は1です。このループでは、数値を0以外の値でインクリメントすると数値が増えますが、この場合は無限ループとなることを前提としています。 – ysth

関連する問題