2016-09-01 21 views
3

ソケットプログラミングを使用して、UDPを介して別のハードウェアに送信するための数値の配列をパックしています。浮動小数点数のパックおよびアンパック時に、浮動小数点の不正確さを排除する方法を教えてください。

私がpackの場合は12.2、次にunpackの場合は12.199999892651となります。私が緯度と経度に関連する数字を扱っているので、私はそのような偏差を持つことはできません。

これは私が書いた簡単なスクリプトは次のとおりです。

use warnings; 

use Time::HiRes qw (sleep); 

@Data = (20.2, 30.23, 40.121, 1, 2, 3, 4, 6. 4, 3.2, 9.9, 0.1, 12.2, 0.99, 7.8, 999, 12.3); 

$myArr = pack('f*', @Data); 

print "$myArr\n\n"; 

@Dec = unpack('f*',$myArr); 

print "@Dec"; 

出力は次のようになります。私は精度を制御することができます任意の方法は

20.2000007629395 30.2299995422363 40.1209983825684 1 2 3 4 6.40000009536743 3.20 000004768372 9.89999961853027 0.100000001490116 12.1999998092651 0.9900000095367 43 7.80000019073486 999 12.3000001907349 

ありますか?

+1

私はあなたが別のポストにあなたの2番目の質問を分割すべきだと思います。最初の質問と実際には関連していません。将来の訪問者がそのような方法で回答を見つけることは容易になります。 – ThisSuitIsBlackNot

+1

12.2は122/10に等しい10進数です。しかし、 'm /(2 ** n)'という表現では表現できないため、Cの浮動小数点型に依存するデータ型を使って正確に表現することはできません。あなたのコードが12.2の瞬間に精度が失われます。内部的に浮動小数点値12.2はありません。我々はベース10で同様の問題を抱えていますが、彼らには慣れ親しんだので、私たちを驚かせることはありません。 1/3を10進数の形式で表す例があります。いくつかの対策では終了しますが、正確ではありません。 – DavidO

+1

あなたの配列 '@ Data'は人工的です。通常は、浮動小数点数ではなくテキスト文字列で構成されます。値を浮動小数点としてパックするのではなく、テキスト "@Data"を送る必要があると思います。「他端」は、それが何であれ、文字列を空白で簡単に分割し、送信されたものを簡単に復元することができます。 – Borodin

答えて

4

短い答えは:山車としてこれらの数字をパックしないでください。 IEEE浮動小数点表現のために精度が失われます。代わりにそれらを「文字小数点」(つまり文字列)に変換し、文字列としてパックします。精度が本当に必要で、数学演算を実行する必要がない場合は、それらをPerlで文字列として保存することもできます。

+0

受信ハードウェアの終わりには、値のみを受け入れることができます...それはその後、私はあなたが、精度のいくつかの損失を受け入れなければならないとしていると思うの文字列 – Asheesh

+0

を受け入れることはできません。あなたは "f"の代わりに "d"(ダブル)パック形式を試してみるかもしれません。 – mwp

+0

@Asheesh:受信側がパックされた浮動小数点しか受け入れることができない場合、精度が失われてしまいます。 – Borodin

7

packfテンプレートは単精度浮動小数点数のためのテンプレートで、ほとんどのプラットフォームで7桁以上の精度が得られます。 dテンプレートは倍精度を提供し、小数点以下15桁まで十分に適しています。

print unpack("f", pack("f",12.2));   # "12.1999998092651" 
print unpack("d", pack("d",12.2));   # "12.2" 

printf "%.20f",unpack("f", pack("f",12.2)); # "12.19999980926513671875" 
printf "%.20f",unpack("d", pack("d",12.2)); # "12.19999999999999928946" 
1

2/10は1/3のような周期的な数値で、10進数の周期数です。それは無限のストレージを取るように浮動小数点数で正確に格納することは不可能です。このよう

は、それがエラーを導入だpackありません。あなたが提供した番号を忠実に保管しています。

$ perl -E'say sprintf "%.20e", 12.2' 
1.21999999999999992895e+01 

$ perl -E'say sprintf "%.20e", unpack "d", pack "d", 12.2' 
1.21999999999999992895e+01 

浮動小数点数を使用する限り、正確に12.2を格納することはできません。

f(単精度、7桁以上の精度)ではなく、d(精度は倍精度、ほぼ16桁)を使用して、ストアを正確に格納できます。 Perlは倍精度を使用していますので、あなたが実際にf代わりのdを使用することにより精度の損失を導入しました。

のでdを使用して、あなたの結果(sprintf "%.10f")を丸めます。

関連する問題