我正在打包一个数字数组,通过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
有什么方法可以控制精度吗?
答案 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"
)。