2010-10-06 110 views
5

我需要做一些低於大十六進制數的算術,但是當我嘗試輸出時,我得到溢出錯誤消息「十六進制數字> 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

回答

12
#!/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

相關問題