Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I do 64-bit hex/decimal arithmetic AND output a full number in HEX as string in Perl?

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 >> 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.

like image 393
therobyouknow Avatar asked Oct 06 '10 15:10

therobyouknow


People also ask

How many hex is 64-bit?

Note 6: A 64-bit (or 8-byte) hex number, sometimes called a "Quad Word," has a maximum value of: 18,446,744,073,709,551,615. This is the largest Hexadecimal number the built-in Windows™ 2000/XP Calculator program can handle(6a).

How are hex numbers calculated?

Hex uses 16 digits including 0-9, just as the decimal system does, but also uses the letters A, B, C, D, E, and F (equivalent to a, b, c, d, e, f) to represent the numbers 10-15. Every hex digit represents 4 binary digits, called nibbles, which makes representing large binary numbers simpler.

What is FF FF in hexadecimal?

My book says the hexadecimal notation FFFF equals 65535 in decimal value.


2 Answers

#!/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";
}
like image 87
Chas. Owens Avatar answered Nov 04 '22 05:11

Chas. Owens


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.

like image 25
sizif Avatar answered Nov 04 '22 07:11

sizif