title: How can I do 64-bit hex/decimal arithmetic AND output a full number in HEX as string in Perl?
body: <p>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.</p>
  
  <p>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.</p>
  
  <pre><code>my $result = 0x00000200A0000000 +
               ( ( $id &amp; 0xFFFFF ) * 2 ) + ( ( $id / 0x100000 ) * 0x40000000 );
  </code></pre>
  
  <p>So, for $id with the following values I should get <code>$result</code>:</p>
  
  <pre><code>$id = 0, $result = 0x00000200A0000000
  $id = 1, $result = 0x00000200A0000002
  $id = 2, $result = 0x00000200A0000004
  </code></pre>
  
  <p>How can I do this?</p>
  
  <p>Here is my inconclusive research results, with reasons why:</p>
  
  <ul>
  <li><p><a href="https://stackoverflow.com/questions/1864245/how-can-i-do-64-bit-arithmetic-in-perl">How can I do 64-bit arithmetic in Perl?</a></p></li>
  <li><p><a href="https://stackoverflow.com/questions/1790380/how-can-i-sum-large-hexadecimal-values-in-perl">How can I sum large hexadecimal values in Perl?</a> Vague, answer not definitively precise and no example.</p></li>
  <li><p><a href="http://perlmonks.org/?node=integer+overflow" rel="nofollow noreferrer">Integer overflow</a>
  non conclusive</p></li>
  <li><p><a href="http://www.nntp.perl.org/group/perl.perl5.porters/2007/06/msg125294.html" rel="nofollow noreferrer">Integer overflow</a>
  non conclusive</p></li>
  <li><p><a href="http://perldoc.perl.org/bigint.html" rel="nofollow noreferrer">bigint</a>
  no info about assignment, arithmetic or output</p></li>
  <li><p><a href="http://perldoc.perl.org/bignum.html" rel="nofollow noreferrer">bignum</a>
  examples not close to my problem.</p></li>
  <li><p><a href="https://stackoverflow.com/questions/828462/how-can-i-sprintf-a-big-number-in-perl">How can I sprintf a big number in Perl?</a>
  example given is not enough info for me: doesn't deal with hex
  assignment or arithmetic.</p></li>
  <li><p><em><a href="http://www.perlmonks.org/?node_id=590611" rel="nofollow noreferrer">Re: secret code generator</a></em>
  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.</p></li>
  <li><p><a href="http://www.perlmonks.org/?node_id=616866" rel="nofollow noreferrer">String to Hex</a>
  Example of using Math::BigInt which
  doesn't work for me - still get
  overflow error.</p></li>
  <li><p><a href="http://www.perlmonks.org/?node_id=742688" rel="nofollow noreferrer">Is there a 64-bit hex()?</a>
  Nearly there - but doesn't deal with
  outputting the large number in hex,
  it only talks of decimal.</p></li>
  <li><p><a href="http://search.cpan.org/perldoc/Math::Fleximal" rel="nofollow noreferrer">CPAN Math:Fleximal</a>
  does the arithmetic, but there doesn't seem to be any means to actually
  output the value still in hex</p></li>
  <li><p><a href="http://perldoc.perl.org/functions/sprintf.html" rel="nofollow noreferrer">sprintf</a>
  Doesn't seem to be able to cope with
  numbers greater than 32-bits, get the
  saturated FFFFFFFF message.</p></li>
  </ul>
  
  <hr/>
  
  <p><strong>Edit: Update - new requirement and supplied solution - please feel free to offer comments</strong></p>
  
  <p>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).</p>
  
  <p>However, another requirement was to be able to convert <em>back</em> from the result to the original id.</p>
  
  <p>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:</p>
  
  <pre><code>#!/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") &lt;&lt; 32);
  }
  
  sub to_bighex {
      my $decimal = shift;
      croak "$decimal is not an unsigned integer"
              unless $decimal =~ /^[0-9]+$/;
  
      my $high = $decimal &gt;&gt; 32;
      my $low  = $decimal &amp; 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 &amp; 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 ) &amp; 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";
  }
  </code></pre>
  
  <p>Try out this code at <a href="http://ideone.com/IMsp6" rel="nofollow noreferrer">http://ideone.com/IMsp6</a>.</p>