Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Perl 当我打包和解包浮点数时,如何消除浮点数的不精确性?_Perl_Pack_Unpack - Fatal编程技术网

Perl 当我打包和解包浮点数时,如何消除浮点数的不精确性?

Perl 当我打包和解包浮点数时,如何消除浮点数的不精确性?,perl,pack,unpack,Perl,Pack,Unpack,我正在打包一个数字数组,以便使用套接字编程通过UDP发送到另一个硬件 当我pack12.2这个数字,然后unpack12.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

我正在打包一个数字数组,以便使用套接字编程通过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

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

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

pack
f
模板适用于单精度浮点数,在大多数平台上,精度高达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"

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”
)。

我认为你应该将第二个问题分成一个单独的帖子;这与第一个问题没有太大关系。未来的访问者将更容易通过这种方式找到答案。12.2是一个十进制数,等于122/10。但是它不能用表达式
m/(2**n)
表示,因此不能使用依赖于C的浮点类型的数据类型准确表示。当代码遇到数字12.2时,精度就丢失了——在内部,没有浮点值12.2这样的东西。我们在10号基地也有类似的问题,但已经习惯了,所以我们并不感到惊讶。例如,用十进制表示1/3:0.33?通过某些度量接近,但不准确。您的数组
@Data
是人为的。通常它由文本字符串组成,而不是浮点数。我认为您需要发送文本
“@Data”
,而不是尝试将值打包为浮点值。“另一端”,不管它是什么,都可以轻松地在空白处分割字符串,并准确地恢复发送的内容。请始终
使用strict
,即使是在最琐碎的Perl程序中,假设您处理的是地球上以度为单位的纬度和经度,然后是12.2和12.199999892651之间的错误,赤道处为12mm。接收硬件端只能接受值。。。它不能接受这些细线,因此我认为你将不得不接受一些准确性的损失。您可以尝试使用“d”(双精度)包格式而不是“f”。@Asheesh:如果接收端只能接受压缩浮点,那么您将陷入精度下降的困境。@Borodin Jinx.:)