sprintf:定点,大数和精度损失

时间:2013-07-25 11:39:35

标签: perl floating-point printf fixed-point

我需要读取数据库中的一些数字,并使用Perl将它们写入文本文件。

在数字表中,数据格式定义为numeric (25,5)(它读取25位数,包括5位小数)。

我使用sprintf "%.5f", $myvalue格式化我的文件中的数字以强制5位小数,我只是注意到对于大数值,超过17位的数字会有精度损失:

db   = 123.12345
file = 123.12345 (OK)

db   = 12345678901234891.12345
file = 12345678901234892.00000 (seems to be rounded to upper integer)

db   = 12345678901234567890.12345
file = 12345678901234567000.00000 (truncation ?)

Perl对固定十进制数的最高精度是什么?

我一般都知道浮点运算的概念和局限,但我不是Perl和尚,我不知道Perl的内部因此我不知道它是否正常(或者它是否相关)根本不是浮点数)。我不确定它是Perl的内部限制,还是与sprintf处理相关的问题。

是否有解决方法或专用模块可以帮助解决该问题?

一些值得注意的要点:

  • 这是已经使用Perl的系统的附加功能,因此使用其他工具不是一个选项
  • 被碾压的数据属于财务状况,所以我需要保留每一分钱而我无法应对+/- 10 000单位精度:^ S

5 个答案:

答案 0 :(得分:2)

再一次,我在问SO之后就找到了解决方案。我在这里提出我的解决方案,以帮助未来的访客:

替换

$myout = sprintf "%.5f", $myvalue;

通过

use Math::BigFloat;
$myout = Math::BigFloat->new($myvalue)->ffround( -5 )->bstr;

答案 1 :(得分:2)

没有像Math :: BigFloat这样的模块,16位以上的数字都是纯粹的魔法......例如。

perl -e 'printf "*10^%02d: %-.50g\n", $_, log(42)*(10**$_) for (0..20)'

产生

*10^00: 3.7376696182833684112267746968427672982215881347656
*10^01: 37.376696182833683224089327268302440643310546875
*10^02: 373.76696182833683224089327268302440643310546875
*10^03: 3737.6696182833684360957704484462738037109375
*10^04: 37376.6961828336861799471080303192138671875
*10^05: 373766.96182833681814372539520263671875
*10^06: 3737669.6182833681814372539520263671875
*10^07: 37376696.18283368647098541259765625
*10^08: 373766961.82833683490753173828125
*10^09: 3737669618.283368587493896484375
*10^10: 37376696182.83368682861328125
*10^11: 373766961828.33685302734375
*10^12: 3737669618283.36865234375
*10^13: 37376696182833.6875
*10^14: 373766961828336.8125
*10^15: 3737669618283368.5
*10^16: 37376696182833688
*10^17: 373766961828336832
*10^18: 3737669618283368448
*10^19: 37376696182833684480
*10^20: 373766961828336828416

答案 2 :(得分:1)

  

Perl对固定十进制数的最高精度是什么?

Perl没有定点十进制数。实际上,很少有语言能做到。您可以使用Math::FixedPoint之类的模块,但

答案 3 :(得分:1)

Perl在内部将您的值存储为浮点数。 1 精度取决于您的Perl版本的编译方式,但它可能是64位的两倍。

C:\>perl -MConfig -E "say $Config::Config{doublesize}"
8

64位双精度浮点数 2 具有53位有效数字(a.k.a。分数或尾数),它给出了大约16个精度的十进制字符。您的数据库定义为存储25个字符的精度。如果您将数据视为字符串,那么您会没事的,但如果将其视为数字,则会失去精确度。

Perl的bignum编译指示为任意大数提供透明支持。它可以大大减慢速度,从而将其使用限制在尽可能小的范围内。如果您只想要大浮点数(不使其他数字类型为“大”),请改用Math::BigFloat

<子> 1.在内部,perl使用一种称为SV的数据类型,它可以同时保存浮点数,整数和/或字符串。
2.假设IEEE 754格式。

答案 4 :(得分:0)

或者,如果您只是将值从数据库传输到文本文件而不是作为数字操作它们,那么让DB将它们格式化为字符串。然后阅读并将它们打印成字符串(也许使用&#34; printf&#39;%s&#39;&#34;)。例如:

select Big_fixed_point_col(format '-Z(24)9.9(5)')(CHAR(32))