打包和解包浮点数时,如何消除浮点不准确?

时间:2016-09-01 19:21:44

标签: perl pack unpack

我正在打包一个数字数组,通过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

有什么方法可以控制精度吗?

3 个答案:

答案 0 :(得分:7)

pack的{​​{1}}模板用于单精度浮点数,在大多数平台上,精度为7位左右。 f模板提供双精度,并且足够大约15个小数位。

d

答案 1 :(得分:4)

简短的回答是:不要将这些数字打包为花车。由于IEEE浮点表示,您将失去准确性。相反,将它们转换为“字符小数”(即字符串),并将它们打包为字符串。如果您确实需要准确性,并且不需要对它们执行数学运算,您可能还希望将它们存储为Perl中的字符串。

答案 2 :(得分:1)

2/10是二进制的周期数,就像1/3是十进制的周期数一样。它不可能完全存储在浮点数中,因为它需要无限存储。

因此,引入错误的不是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。

但正如您在上面所看到的,您可以使用d(双精度,几乎16位精度)而不是f精确存储商店(单精度,超过7位数)精确)。 Perl使用双精度,因此您实际使用f代替d来引入精度损失。

因此请使用d,并对结果进行舍入(sprintf "%.10f")。