in reply to Integer overflow

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

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

Replies are listed 'Best First'.
Re^2: Integer overflow
by gone2015 (Deacon) on Apr 24, 2009 at 11:16 UTC
    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) ; } ;

        OK...

        { my $q = $result & 0xFFFF_FFFF ; $result = $q <= 0x7FFF_FFFF ? $q : -1 - ($q ^ 0xFFFF_FFFF) ; } ;
        ...the objective is to take the LS 32 bits of an integer, 32 bit or more, signed or unsigned, and set $result to be the signed 32 bit integer value. So:

        1. $result & 0xFFFF_FFFF extracts the LS 32 bits and returns an unsigned value.

        2. if the value is now <= 0x7FFF_FFFF, then it's positive, and we're done...

        3. ...otherwise we have 0x8000_0000..0xFFFF_FFFF which we need to map to -0x8000_0000..-0x0000_0001. The XOR $q ^ 0xFFFF_FFFF gives 0x7FFF_FFFF..0x0000_0000, so we complete the process by negating that and subtracting 1... or subtracting that from -1.

          This could also be written -(($q ^ 0xFFFF_FFFF) + 1)or -((~$q + 1) & 0xFFFF_FFFF)... which are closer to the conventional way of changing the sign -- noting that we want this to work for any size of integer from 32 up.

        This is assuming 2's complement signed integers. I cannot recall a sign and magnitude machine (for integers). The last 1's complement machine I worked on was a CDC6400 in the 70's. So it's a fairly safe assumption.

        But, you could always: unpack('l', pack('L', $result & 0xFFFF_FFFF)), which ensures a valid argument for pack('L', ...) and makes no assumptions about how unpack('l', ...) interprets the bits.

Re^2: Integer overflow
by terranullis (Initiate) on Apr 23, 2009 at 23:45 UTC
    Wow! That was fast. Thank you so much.