I need to do some arithmetic with large hexadecimal numbers below, but when I try to output I'm getting overflow error messages "Hexadecimal number > 0xffffffff non-portable", messages about not portable, or the maximum 32-bit hex value FFFFFFFF.
All of which imply that the standard language and output routines only cope with 32 bit values. I need 64-bit values and have done a lot of research, but I found nothing that BOTH enables the arithmetic AND outputs the large number in hex.
my $result = 0x00000200A0000000 +
( ( $id & 0xFFFFF ) * 2 ) + ( ( $id / 0x100000 ) * 0x40000000 );
So, for $id with the following values I should get $result
:
$id = 0, $result = 0x00000200A0000000
$id = 1, $result = 0x00000200A0000002
$id = 2, $result = 0x00000200A0000004
How can I do this?
Here is my inconclusive research results, with reasons why:
How can I do 64-bit arithmetic in Perl?
How can I sum large hexadecimal values in Perl? Vague, answer not definitively precise and no example.
Integer overflow non conclusive
Integer overflow non conclusive
bigint no info about assignment, arithmetic or output
bignum examples not close to my problem.
How can I sprintf a big number in Perl? example given is not enough info for me: doesn't deal with hex assignment or arithmetic.
Re: secret code generator Some examples using Fleximal, mentions to_str to output value of variable but 1) I don't see how the variable was assigned and 2) I get error "Can't call method "to_str" without a package or object reference" when I run my code using it.
String to Hex Example of using Math::BigInt which doesn't work for me - still get overflow error.
Is there a 64-bit hex()? Nearly there - but doesn't deal with outputting the large number in hex, it only talks of decimal.
CPAN Math:Fleximal does the arithmetic, but there doesn't seem to be any means to actually output the value still in hex
sprintf Doesn't seem to be able to cope with numbers greater than 32-bits, get the saturated FFFFFFFF message.
Edit: Update - new requirement and supplied solution - please feel free to offer comments
Chas. Owens answer is still accepted and excellent (part 2 works for me, haven't tried the part 1 version for newer Perl, though I would invite others to confirm it).
However, another requirement was to be able to convert back from the result to the original id.
So I've written the code to do this, here's the full solution, including @Chas. Owens original solution, followed by the implementation for this new requirement:
#!/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 >>开发者_Go百科; 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";
}
Try out this code at http://ideone.com/IMsp6.
#!/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;
}
The bigint
pragma replaces the hex
function with a version that can handle numbers that large. It also transparently makes the mathematical operators deal with big ints instead of the ints on the target platform.
Note, this only works in Perl 5.10 and later. If you are running an earlier version of Perl 5, you can try this:
#!/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";
}
The comment by ysth is right. Short example of 64-bit arithmetics using Perl from Debian stretch without Math::BigInt aka "use 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;
The script prints 0xffffffffffffffffffffffffffffffd0.
精彩评论