使用Perl分割浮点数

5
为什么使用split后我失去了精度? 我的目标是获得小数部分,全部的小数部分。
$a = 123456789.123456789;
@b = split(/\./, $a);
$baseDec = "." . $b[1];

以上给出 $baseDec == .123457

但是以下方法可以得到正确的精度:

$baseDec = round($baseDec, 10);

这样做是正确的吗?更正:这个方法会导致相同的错误精度!我没有正确测试代码,抱歉!

$a = 123456789.123456789;
@b = split(/\./, $a);
$baseInt = $b[0];
$baseDec = $a - $baseInt;

我应该使用Math::BigFloat吗?

编辑: $a 应该是一个字符串 $a = "123456789.123456789";,然后原始代码就可以工作了。在我解决如何让我的Perl与longdouble一起工作之前,我无法测试原始问题。答案似乎是因为$a被存储在双精度浮点数中(52位~15个十进制数字,如@Ben在下面所述)。print $a会输出123456789.123457

4个回答

4
您可以使用 int 函数:
 $a = 123456789.123456789;
 $baseDec = $a - int($a);

你有测试过吗?当我尝试使用 printf("%.9f", $baseDec); 时,结果是 0.123456791 而不是 0.123456789 - Peter Mortensen

4
您失去了精度,因为123456789.123456789是一个数字文字,Perl编译器默认将其存储在double(52位,约15个十进制数字)的变量$a中。接下来,当运行@b = split(/\./, $a);时,$a会被隐式强制转换成字符串以便于进行split操作。
如果您的Perl编译使用了longdouble(请参见:perl -V:uselongdoubleperl -V:doublesize),则该数字文字将用80位(约21个十进制数字)表示,并且需要进行字符串强制转换。

在 Linux 上执行 perl -V 命令,输出和你的一样。在 Sun4-Solaris 上,它是(字节序颠倒 - 不是问题):intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=87654321``d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16 - dnvrdave
你的原始代码在Solaris上能正常工作吗? - Ben Grimm
1
不好意思,我的代码并没有按照所述的方式工作。我犯了一个错误,没有进行适当的测试就写出了这个问题!$a 应该是一个字符串,然后代码才能正常工作。我想 longdouble 没有被使用。print $a 的输出结果是 123456789.123457。感谢 @Ben 花时间揭开一个具有欺骗性的问题! - dnvrdave

3

如果您要将其视为字符串,请始终这样做。您不会没有引号分配字符串,对吧?

my $a = "123456789.123456789";

是的,你看穿了我的无意中误导性的问题!在我的代码中,$a实际上是一个字符串,如果我保持这种方式,它可以正常工作。 - dnvrdave

1
你可以通过使用sprintf来获取它:
my $a = 123456789.123456789;
my @b = split(/\./, sprintf("%.8f",$a));
my $baseDec = "." . $b[1];
print $baseDec;

你是不是指的是"%.9f"? - Peter Mortensen
即使如此,结果仍不正确(为.123456791而非.123456789)。printf("%.15f", $a) 的结果为 123456789.123456791043282 - Peter Mortensen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接