Search code examples
perl32-bitinteger-overflowunsigned-integer

How to do unsigned 32 bit arithmetic in Perl?


I would like to implement the following C program in Perl:

#include <stdio.h>
#include <stdint.h>

uint32_t xorshift32 ()
{
  static uint32_t y = 2463534242;
  y ^= y << 13;
  y ^= y >> 17;
  y ^= y << 5;
  return y;
}

int main (int argc, char *argv[])
{
  int n = 10;
  while (n-- > 0)
    printf ("%u\n", xorshift32());
}

The output is:

723471715
2497366906
2064144800
2008045182
3532304609
374114282
1350636274
691148861
746858951
2653896249

This is my unsuccessful attempt:

{
  package Xorshift;
  use strict;
  use warnings;
  use integer;

  sub new
  {
    my $class = shift;
    bless { y => 2463534242 } => $class
  }

  sub rand ()
  {
    my $y = $_[0]->{y};
    $y ^= $y << 13;
    $y ^= $y >> 17;
    $y ^= $y << 5;
    $_[0]->{y} = $y;
    return $y;
  }
}

my $xor = Xorshift->new;

my $n = 10;
while ($n-- > 0) {
  print $xor->rand(), "\n";
}

The output is this:

660888219700579
3396719463693796860
-1120433007023638100
2588568168682748299
1469630995924843144
-8422345229424035168
1449080611344244726
-4722527344582589597
8061824971057606814
-3113862584906767882

The problems:

  1. Perl uses 64 bit arithmetic.
  2. The integers are signed.

How to do 32 bit unsigned arithmetic instead?


Solution

  • If you want to simulate the result of 32-bit ops, you can simply apply a mask:

    {
      package Xorshift;
      use strict;
      use warnings;
      use integer;
    
      sub new
      {
        my $class = shift;
        bless { y => 2463534242 } => $class
      }
      
      sub to32{
        return ($_[0] & 0xFFFFFFFF);
      }
    
      sub rand ()
      {
        my $y = $_[0]->{y};
        $y ^= to32($y << 13);
        $y ^= to32($y >> 17);
        $y ^= to32($y << 5);
        $_[0]->{y} = $y;
        return $y;
      }
    }
    
    my $xor = Xorshift->new;
    
    my $n = 10;
    while ($n-- > 0) {
      print $xor->rand(), "\n";
    }