terranullis has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,

First time poster, and a Perl newbie. I am trying to port some code from Obj-C/Cocoa and ran into a problem with an integer overflow. In Cocoa the result (signed integer) is being wrapped to a negative. In Perl, it is not:

$result = 1729080737 + 72 * 14 * 425567; #result (Perl) = 2158052273 #result (Cocoa) = -2136915023

Is there any way I can get the same result in Perl? I have a very vague understanding of this but hope I have described the problem correctly. Thanks. /John

Replies are listed 'Best First'.
Re: Integer overflow
by almut (Canon) on Apr 23, 2009 at 23:42 UTC
    my $result = unpack "l", pack "l", 1729080737 + 72 * 14 * 425567; # -2136915023

    (the pack template "l" stands for 32-bit signed integer)

      my $result = unpack "l", pack "l", 1729080737 + 72 * 14 * 425567;

      In this case the arithmetic gives an unsigned 32-bit integer, and all is well...

      ...though I note it works because pack('l', ...) silently accepts a value outside -0x8000_0000..+0x7FFF_FFFF and packs away the LS 4 bytes of it's machine representation. I haven't found where that is documented... In this case that can be avoided by using pack('L', ...).

      I wondered about more general/extreme cases, for example:

      my $result = 1729080737 + 72 * 14 * 425567 + 0xFFFF_FFFF ; print $result, "\n" ; print $result & 0xFFFF_FFFF, "\n" ; print unpack('l', pack('l', $result)), "\n" ;
      which works fine on 64 bit integer systems, but on a 32 bit one the result is:
      6453019568
      4294967295
      -1
      
      because Perl happily does the arithmetic in ~54 bits of "integer in floating point form" (assuming IEEE-754 double), but when an integer in integer form is required, gives 0xFFFF_FFFF for anything > 0xFFFF_FFFF (and -0x8000_0000 for anything < -0x8000_0000) even if the operation would happily mask down to 32 bits :-( I don't know of a simple way to persuade Perl to give the LS 32 bits of an integer which may or may not be held as a float.

      use integer wrapped around the arithmetic gives the expected result on both 32 and 64 bit systems.

      Avoiding pack (in order to avoid giving an out of range argument) is a bit messy, but I believe this works for both 64 and 32 bit systems:

      { my $q = $result & 0xFFFF_FFFF ; $result = $q <= 0x7FFF_FFFF ? $q : -1 - ($q ^ 0xFFFF_FFFF) ; } ;

        Well this explains why none of my standard approaches to determining overflow work in Perl. I had discovered that any overflow was capped rather than rolling over but I was pulling my hair our for a while as to 'WHY' until I googled for 'perl integer overflow' and found this thread. Many Thanks !!

        Misha/Michael - Russian student, grognard, bemused observer of humanity and self professed programmer with delusions of relevance

        I'm trying to be less obtuse about this kind of thing; finally buckle down and learn applied bit ops and hex math and friends. Do you think you could walk (me) through exactly what's going on in this block?

        { my $q = $result & 0xFFFF_FFFF ; $result = $q <= 0x7FFF_FFFF ? $q : -1 - ($q ^ 0xFFFF_FFFF) ; } ;
      Wow! That was fast. Thank you so much.
Re: Integer overflow
by ikegami (Patriarch) on Apr 24, 2009 at 02:08 UTC
Re: Integer overflow
by roboticus (Chancellor) on Apr 23, 2009 at 23:44 UTC
    terranullis:

    Any particular reason you want the wrong answer? I would imagine that you could use whatever Obj-C's equivalent to long long to get 64-bit integers. But if you really want 32-bit integers, and want perl to have the same values, you could probably use pack and unpack to truncate the data and interpret it as a signed 32-bit value. (I can't tell you the details off the top of my head.)

    ...roboticus
      Yes, I need the wrong answer - it's a license validation thing. I will try the pack/unpack code suggested by almut. I just got started with perl about two minutes ago so this is all news to me. Thanks a lot for helping out!
Re: Integer overflow
by syphilis (Archbishop) on Apr 24, 2009 at 00:14 UTC
    Also:
    use integer; $result = 1729080737 + 72 * 14 * 425567; print $result;
    But this is possibly a case of misuse integer; instead :-)

    Cheers,
    Rob

      I think this would only work on 32-bit machines (or rather, 32-bit perls).  At least, on my 64-bit box, I get 2158052273.

        At least, on my 64-bit box, I get 2158052273

        Yes, on my 64-bit build on linux (32-bit architecture) I'd have to use your solution.

        On an architecture where sizeof(long)==8, I would think that neither solution would work. (This is probably, though not necessarily, an irrelevant observation in the context of what the op needs :-)

        Cheers,