【问题标题】:How can I do 64-bit hex/decimal arithmetic AND output a full number in HEX as string in Perl?如何进行 64 位十六进制/十进制算术并在 Perl 中将十六进制的完整数字作为字符串输出?
【发布时间】:2011-04-21 21:53:33
【问题描述】:

我需要在下面使用较大的十六进制数进行一些算术运算,但是当我尝试输出时,我收到溢出错误消息“十六进制数 > 0xffffffff 不可移植”、有关不可移植的消息或最大 32 位十六进制数值 FFFFFFFF。

所有这些都意味着标准语言和输出例程只能处理 32 位值。我需要 64 位值并进行了大量研究,但我没有发现 BOTH 启用算术并以十六进制输出大数字。

my $result = 0x00000200A0000000 +
             ( ( $id & 0xFFFFF ) * 2 ) + ( ( $id / 0x100000 ) * 0x40000000 );

因此,对于具有以下值的 $id,我应该得到$result

$id = 0, $result = 0x00000200A0000000
$id = 1, $result = 0x00000200A0000002
$id = 2, $result = 0x00000200A0000004

我该怎么做?

这是我不确定的研究结果,原因如下:


编辑:更新 - 新要求和提供的解决方案 - 请随时提供 cmets

查斯。 Owens 的回答仍然被接受并且非常出色(第 2 部分对我有用,还没有尝试过第 1 部分的新 Perl 版本,尽管我会邀请其他人确认)。

但是,另一个要求是能够将 back 从结果转换为原始 id。

所以我已经编写了代码来执行此操作,这是完整的解决方案,包括@Chas。 Owens 的原始解决方案,随后是针对此新要求的实施:

#!/usr/bin/perl

use strict;
use warnings;
use bigint;

use Carp;

sub bighex {
    my $hex = shift;

    my $part = qr/[0-9a-fA-F]{8}/;
    croak "$hex is not a 64-bit hex number"
        unless my ($high, $low) = $hex =~ /^0x($part)($part)$/;

    return hex("0x$low") + (hex("0x$high") << 32);
}

sub to_bighex {
    my $decimal = shift;
    croak "$decimal is not an unsigned integer"
            unless $decimal =~ /^[0-9]+$/;

    my $high = $decimal >> 32;
    my $low  = $decimal & 0xFFFFFFFF;

    return sprintf("%08x%08x", $high, $low);
}

for my $id (0 ,1, 2, 0xFFFFF, 0x100000, 0x100001, 0x1FFFFF, 0x200000, 0x7FDFFFFF ) {
    my $result = bighex("0x00000200A0000000");
    $result += ( ( $id & 0xFFFFF ) * 2 ) + ( ( $id / 0x100000 ) * 0x40000000 );

    my $clusterid = to_bighex($result);

# the convert back code here:
my $clusterid_asHex = bighex("0x".$clusterid);
my $offset = $clusterid_asHex - bighex("0x00000200A0000000");
my $index_small_units = ( $offset / 2 ) & 0xFFFFF;
my $index_0x100000_units = ( $offset / 0x40000000 ) * 0x100000;
my $index = $index_0x100000_units + $index_small_units;


    print "\$id = ".to_bighex( $id ).
          " clusterid = ".$clusterid.
          " back to \$id = ".to_bighex( $index ).
          " \n";
}

http://ideone.com/IMsp6 试用此代码。

【问题讨论】:

  • 这不是错误消息,而是警告。具体来说,您的代码可能在 perl 使用 64 位整数的情况下工作,但在使用 32 位整数的情况下不能工作。如果事实上您拥有并且将始终拥有 64 位整数,请使用 no warnings "portable"; 禁用它
  • @ysth 这是个坏主意。代码将不再是可移植的。通过关闭它们来消除警告是一种不好的做法。请参阅我的答案以获得更好的解决方案。
  • 尽管接受了另一个答案,但我还是礼貌地回复您: 仅作记录:听取您关于没有警告“便携”的建议;但仍然收到错误消息:“./code.pl 第 9 行的十六进制数字中的整数溢出” - 使用的代码:#!/usr/bin/perl -w use strict;使用警告;没有警告“便携”;我的 $id = 1;我的 $result = 0x0000_0200_A000_0000 + ( ( $id & 0xFFFFF ) * 2 ) + ( ( $id / 0x100000 ) * 0x4000_0000 );

标签: perl math 64-bit printf


【解决方案1】:
#!/usr/bin/perl

use strict;
use warnings;

use bigint qw/hex/;

for my $id (0 ,1, 2) {
    my $result = hex("0x00000200A0000000") + 
        ( ( $id & 0xFFFFF ) * 2 ) + ( ( $id / 0x100000 ) * 0x40000000 );
    printf "%d: %#016x\n", $id, $result;
}

bigint pragma 将hex 函数替换为可以处理这么大数字的版本。它还透明地使数学运算符处理大整数而不是目标平台上的整数。

注意,这只适用于 Perl 5.10 及更高版本。如果你运行的是 Perl 5 的早期版本,你可以试试这个:

#!/usr/bin/perl

use strict;
use warnings;
use bigint;

use Carp;

sub bighex {
    my $hex = shift;

    my $part = qr/[0-9a-fA-F]{8}/;
    croak "$hex is not a 64-bit hex number"
        unless my ($high, $low) = $hex =~ /^0x($part)($part)$/;

    return hex("0x$low") + (hex("0x$high") << 32);
}

sub to_bighex {
    my $decimal = shift;
    croak "$decimal is not an unsigned integer"
            unless $decimal =~ /^[0-9]+$/;

    my $high = $decimal >> 32;
    my $low  = $decimal & 0xFFFFFFFF;

    return sprintf("%08x%08x", $high, $low);
}

for my $id (0 ,1, 2) {
    my $result = bighex("0x00000200A0000000");
    $result += ( ( $id & 0xFFFFF ) * 2 ) + ( ( $id / 0x100000 ) * 0x40000000 );
    print "$id ", to_bighex($result), "\n";
}

【讨论】:

  • 有时您不需要便携性。在支持 64 位的 perl 上使用 bigint 锤子做 64 位数学对我没有吸引力。您所做的 printf() 调用已经将可移植性抛到了窗外。
  • @ysth 是的,我看到了,并编写了第二个版本,它应该适用于所有 32 位机器。
  • @Chas.Owens。 - 非常感谢你这么快回答,但我得到:/usr/lib/perl5/5.8.8/bigint.pm 第 142 行的未知选项十六进制。BEGIN 失败--编译在 XXXX 第 23 行中止(第 23 行:使用 bigint qw/hex/;
  • 这意味着您使用的是 Perl 5.8.9 或更低版本。您将需要使用第二个版本。 hex 替换仅适用于 Perl 5.10 或更高版本。
  • Brilliant - 第二个对我有用。我正在使用带有 v5.8.8 的 CentOS。 +1 并接受答案。我测试了它的一系列值,一切都很好。干得好,@Chas。欧文斯。非常感谢。稍后,我想升级到 5.10,但作为生产服务器,这可能会对在其上运行的实时应用程序造成风险。
【解决方案2】:

ysth的评论是对的。使用来自 Debian 的 Perl 的 64 位算术的简短示例,没有 Math::BigInt 又名“使用 bigint”:

#!/usr/bin/perl -wwi

sub do_64bit_arith {
    use integer;
    my $x = ~2;
    $x <<= 4;
    printf "0x%08x%08x\n", $x>>32, $x;
}

do_64bit_arith();
exit 0;

脚本打印 0xffffffffffffffffffffffffffffffd0。

【讨论】:

    猜你喜欢
    • 2019-07-27
    • 2017-08-12
    • 1970-01-01
    • 2014-03-19
    • 2015-11-29
    • 2019-02-21
    • 2015-04-30
    相关资源
    最近更新 更多