Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number

Re^2: Integer overflow

by gone2015 (Deacon)
on Apr 24, 2009 at 11:16 UTC ( [id://759804] : note . print w/replies, xml ) Need Help??

in reply to Re: Integer overflow
in thread Integer overflow

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:
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) ; } ;

Replies are listed 'Best First'.
Re^3: Integer overflow
by MishaMoose (Scribe) on Dec 03, 2010 at 15:03 UTC

    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
Re^3: Integer overflow
by Your Mother (Archbishop) on Apr 24, 2009 at 20:44 UTC

    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) ; } ;


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