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) | [reply] [d/l] [select] |
|
|
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) ;
} ;
| [reply] [d/l] [select] |
|
|
| [reply] |
|
|
{ my $q = $result & 0xFFFF_FFFF ;
$result = $q <= 0x7FFF_FFFF ? $q : -1 - ($q ^ 0xFFFF_FFFF) ;
} ;
| [reply] [d/l] |
|
|
|
|
Wow! That was fast. Thank you so much.
| [reply] |
Re: Integer overflow
by ikegami (Patriarch) on Apr 24, 2009 at 02:08 UTC
|
$result -= 2**32 if $result >= 2**31;
| [reply] [d/l] |
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 | [reply] |
|
|
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!
| [reply] |
Re: Integer overflow
by syphilis (Archbishop) on Apr 24, 2009 at 00:14 UTC
|
use integer;
$result = 1729080737 + 72 * 14 * 425567;
print $result;
But this is possibly a case of misuse integer; instead :-)
Cheers, Rob | [reply] [d/l] [select] |
|
|
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.
| [reply] |
|
|
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,
| [reply] |
|
|
|
|