我怎样才能在 Perl 中 sprintf 一个大数字?

发布于 2024-07-19 04:24:52 字数 283 浏览 9 评论 0原文

在 Windows 32 位平台上,我必须读取一些数字,这是出乎意料的,它们的值可以大到 99,999,999,999,但不能再大了。 尝试 sprintf("%011d", $myNum) 它们会输出溢出:-2147483648。

我无法使用 BigInt 模块,因为在这种情况下我应该深入更改代码。 我无法将格式管理为字符串 sprintf("%011s", $numero),因为减号处理不正确。

我该如何处理这个问题? 打包/解包有帮助吗?

On a Windows 32-bit platform I have to read some numbers that, this was unexpected, can have values as big as 99,999,999,999, but no more. Trying to sprintf("%011d", $myNum) them outputs an overflow: -2147483648.

I cannot use the BigInt module because in this case I should deeply change the code. I cannot manage the format as string, sprintf("%011s", $numero), because the minus sign is incorrectly handled.

How can I manage this? Could pack/unpack be of some help?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

年少掌心 2024-07-26 04:24:53

尝试将其格式化为不带小数部分的浮点数:

$ perl -v
This is perl, v5.6.1 built for sun4-solaris
...

$ perl -e 'printf "%011d\n", 99999999999'
-0000000001

$ perl -e 'printf "%011.0f\n", 99999999999'
99999999999

Try formatting it as a float with no fraction part:

$ perl -v
This is perl, v5.6.1 built for sun4-solaris
...

$ perl -e 'printf "%011d\n", 99999999999'
-0000000001

$ perl -e 'printf "%011.0f\n", 99999999999'
99999999999
天煞孤星 2024-07-26 04:24:53

是的,Perl 的数字盲点之一是格式化; Perl 自动很好地处理将数字表示为整数或浮点数,但随后将它们强制转换为
使用 printf 数字格式时使用其中之一,即使不是
合适的。 并且 printf 根本不真正处理 BigInts (除非通过处理
将它们作为字符串并将其转换为数字,但会损失精度)。

使用 %s 而不是 %d 与您不确定是否合适的任何数字
range 是一个很好的解决方法,除非您注意到负数。 处理
那些,你将不得不编写一些 Perl 代码。

Yes, one of Perl's numeric blind spots is formatting; Perl automatically handles representing numbers as integers or floats pretty well, but then coerces them into
one or the other when the printf numeric formats are used, even when that isn't
appropriate. And printf doesn't really handle BigInts at all (except by treating
them as strings and converting that to a number, with loss of precision).

Using %s instead of %d with any number you aren't sure will be in an appropriate
range is a good workaround, except as you note for negative numbers. To handle
those, you are going to have to write some Perl code.

我恋#小黄人 2024-07-26 04:24:53

在一定程度上,浮动是可行的。

perl -e "printf qq{%.0f\n}, 999999999999999"
999999999999999

但只有在一定程度上

perl -e "printf qq{%.0f\n}, 9999999999999999999999999999999999999999999999"
9999999999999998663747590131240811450955988992

Bignum 在这里没有帮助。

perl -e "use bignum ; printf qq{%.0f\n}, 9999999999999999999999999999999999999999999999"
9999999999999999931398190359470212947659194368    

问题是printf。 (你真的需要 printf 吗?)
打印可以吗?

perl -e "use bignum;print 9999999999999999999999999999999999999999999999"
9999999999999999999999999999999999999999999999

说了这么多,perl 的好处是它始终是您自己开发的一个选择。

例如

my $in = ...;
my $out = "";
while($in){
  my $chunk=$in & 0xf;
  $in >>= 4;
  $out = sprintf("%x",$chunk).$out;
}
print "0x$out\n";

Floats can work, up to a point.

perl -e "printf qq{%.0f\n}, 999999999999999"
999999999999999

But only up to a point

perl -e "printf qq{%.0f\n}, 9999999999999999999999999999999999999999999999"
9999999999999998663747590131240811450955988992

Bignum doesn't help here.

perl -e "use bignum ; printf qq{%.0f\n}, 9999999999999999999999999999999999999999999999"
9999999999999999931398190359470212947659194368    

The problem is printf. (Do you really need printf?)
Could print work?

perl -e "use bignum;print 9999999999999999999999999999999999999999999999"
9999999999999999999999999999999999999999999999

Having said all of that, the nice thing about perl is it's always an option to roll your own.

e.g.

my $in = ...;
my $out = "";
while($in){
  my $chunk=$in & 0xf;
  $in >>= 4;
  $out = sprintf("%x",$chunk).$out;
}
print "0x$out\n";
初吻给了烟 2024-07-26 04:24:53

我不是 Perl 专家,也许我在这里缺少某种 bignum 的自动处理,但这不是简单的整数溢出情况吗? 32 位整数不能容纳 99,999,999,999 这样大的数字。

无论如何,我在 32 位 Linux 机器上使用 Perl v5.8.8 得到了相同的结果,并且带有“%d”的 printf 似乎不能处理更大的数字。

I'm no Perl expert, and maybe I'm missing some sort of automatic handling of bignums here, but isn't this simply a case of integer overflow? A 32-bit integer can't hold numbers that are as big as 99,999,999,999.

Anyway, I get the same result with Perl v5.8.8 on my 32-bit Linux machine, and it seems that printf with "%d" doesn't handle larger numbers.

烂柯人 2024-07-26 04:24:53

我认为你的 Perl 副本一定被破坏了,这是来自 CygWin 的版本(5.10):

pax$ perl -e 'printf("%011d\n", 99999999999);'
99999999999

pax$ perl -v

This is perl, v5.10.0 built for cygwin-thread-multi-64int
(with 6 registered patches, see perl -V for more detail)

Copyright 1987-2007, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

你正在运行什么版本(perl -v 的输出)?

您可能需要获得启用 64 位的 Perl 版本 [可能还需要新的 64 位生产机器](请注意我的输出中的 "cygwin-thread-multi-64int")。 这至少可以避免更改代码的需要。

我这样说是因为您不想大幅更改代码(即您担心破坏代码)。 新硬件的解决方案虽然有点昂贵,但几乎肯定不需要您根本更改软件。 这取决于您的优先事项。

另一种可能性是 Perl 本身可能正确存储数字,但由于 printf() 缺陷而显示错误。 在这种情况下,您可能想尝试:

$million = 1000000;
$bignum = 99999999999;
$firstbit = int($bignum / $million);
$secondbit = $bignum - $firstbit * million;
printf ("%d%06d\n",$firstbit,$secondbit);

将其放入函数中并调用该函数以返回字符串,例如:

sub big_honkin_number($) {
    $million = 1_000_000;
    $bignum = shift;
    $firstbit = int($bignum / $million);
    $secondbit = $bignum - $firstbit * $million;
    return sprintf("%d%06d\n", $firstbit, $secondbit);
}
printf ("%s", big_honkin_number (99_999_999_999));

请注意,我对此进行了测试,但在 64 位平台上 - 您需要在 64 位平台上进行自己的测试32 位,但您可以使用您想要的任何缩放因子(如果需要,包括两个以上的段)。

更新: big_honkin_number() 技巧在 32 位 Perl 上运行良好,因此看起来就是只是 printf() 让你感到厌烦的函数:

pax@pax-desktop:~$ perl -v

This is perl, v5.8.8 built for i486-linux-gnu-thread-multi

Copyright 1987-2006, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

pax@pax-desktop:~$ perl qq.pl
99999999999

I think your copy of Perl must be broken, this is from CygWin's version (5.10):

pax$ perl -e 'printf("%011d\n", 99999999999);'
99999999999

pax$ perl -v

This is perl, v5.10.0 built for cygwin-thread-multi-64int
(with 6 registered patches, see perl -V for more detail)

Copyright 1987-2007, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

What version are you running (output of perl -v)?

You may have to get a 64-bit enabled version of Perl [and possibly a new 64-bit production machine] (note the "cygwin-thread-multi-64int" in my output). That will at least avoid the need for changing the code.

I'm stating this on the basis that you don't want to change the code greatly (i.e., you fear breaking things). The solution of new hardware, whilst a little expensive, will almost certainly not require you to change the software at all. It depends on your priorities.

Another possibility is that Perl itself may be storing the number correctly but just displaying it wrong due to a printf() foible. In that case, you may want to try:

$million = 1000000;
$bignum = 99999999999;
$firstbit = int($bignum / $million);
$secondbit = $bignum - $firstbit * million;
printf ("%d%06d\n",$firstbit,$secondbit);

Put that in a function and call the function to return a string, such as:

sub big_honkin_number($) {
    $million = 1_000_000;
    $bignum = shift;
    $firstbit = int($bignum / $million);
    $secondbit = $bignum - $firstbit * $million;
    return sprintf("%d%06d\n", $firstbit, $secondbit);
}
printf ("%s", big_honkin_number (99_999_999_999));

Note that I tested this but on the 64-bit platform - you'll need to do your own test on 32-bit but you can use whatever scaling factor you want (including more than two segments if need be).

Update: That big_honkin_number() trick works fine on a 32-bit Perl so it looks like it is just the printf() functions that are stuffing you up:

pax@pax-desktop:~$ perl -v

This is perl, v5.8.8 built for i486-linux-gnu-thread-multi

Copyright 1987-2006, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

pax@pax-desktop:~$ perl qq.pl
99999999999
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文