我该如何做64位十六进制/十进制算术,并在Perl中输出十六进制的完整数字作为字符串?

问题描述:

我需要做一些低于大十六进制数的算术,但是当我尝试输出时,我得到溢出错误消息“十六进制数字> 0xffffffff不可移植”,有关不可移植的消息或最大32位十六进制值FFFFFFFF。我该如何做64位十六进制/十进制算术,并在Perl中输出十六进制的完整数字作为字符串?

所有这些都意味着标准语言和输出例程只能处理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 

我怎样才能做到这一点?

这里是我不确定的研究成果,与理由:


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

查斯。欧文斯的回答仍然被接受和优秀(第2部分为我工作,还没有尝试新的Perl的第1部分版本,但我会邀请其他人来确认它)。

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

所以我写了代码来做到这一点,下面是完整的解决方案,包括@Chas。欧文斯原来的解决方案,其次是实施这项新规定:

#!/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尝试这个代码。

+1

'perl -V:ivsize'的输出? – ysth 2010-10-06 15:39:25

+0

这不是一个错误消息,它是一个警告。具体来说,你的代码可能工作在perl使用64位整数但不使用32位整数的地方。如果实际上你有和将总是有64位整数,用'无警告'禁用它'';' – ysth 2010-10-06 15:42:48

+0

@ysth这是一个坏主意。那么代码将不再是可移植的。通过关闭警告来消除警告是一种不好的做法。查看我的答案以获得更好的解决方案 – 2010-10-06 15:46:26

#!/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编译使用,可以处理的数字那么大的一个版本替换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"; 
} 
+1

+1:优秀的答案... – dawg 2010-10-06 15:50:02

+1

有时你不需要可移植性。并且在64位支持perl中达成bigint锤子来做64位数学计算对我没有吸引力。那printf()调用你已经把可移植性扔出了窗口。 – ysth 2010-10-06 15:54:32

+1

@ysth是的,我看到了,并写了第二个版本,应该适用于所有的32位机器。 – 2010-10-06 16:15:56