Integer arithmetic
Bengt Richter
bokr at oz.net
Sat Mar 29 19:01:20 EST 2003
On Thu, 27 Mar 2003 07:11:40 GMT, Alex Martelli <aleax at aleax.it> wrote:
>Bengt Richter wrote:
>
>> On Wed, 26 Mar 2003 12:38:25 GMT, Alex Martelli <aleax at aleax.it> wrote:
>>>Daniel Timothy Bentley wrote:
>> [...]
>>>
>>>> Basically, if you're going to say ints and longs are separate types, I
>>>> think there should be a way to makes ints from longs fairly reliably. I
>>>> don't think that's a niche, I think it's something a consistent language
>>>> should provice.
>>>
>>>Well, your proposed expression DOES "make ints from longs fairly
>>>reliably", it's just that I think the ints it makes are probably not the
>>>ones you'd like to get (except for 0<=foo<sys.maxint).
>>>
>>>I think you want something like: int(cmp(foo,0)*(abs(foo) & sys.maxint)).
>>>
>>>Me, I think it's better to incapsulate the "conversion to int ignoring
>>>overflow" in a function, using an if/else:
>>>
>>>def toint(foo):
>>> if foo>=0: return int(foo&sys.maxint)
>>> else: return -int(-foo&sys.maxint)
>>>
>> The above doesn't match typical hardware and C-style int behavior:
>
>Right, it doesn't replicate the asymmetry thus typically found. You
>have to specialcase that. In 2.2 it's easiest and fastest to:
>
>def toint(foo):
> try: return int(foo)
> except OverflowError: pass
> if foo>=0: return int(foo&sys.maxint)
> else: return -int(-foo&sys.maxint)
>
>but in 2.3 int(foo) won't raise OverflowError, so if you need to
>reproduce the asymmetry you can do something like:
>
>def toint(foo):
> if foo==-sys.maxint-1: return int(foo)
> elif foo>=0: return int(foo&sys.maxint)
> else: return -int(-foo&sys.maxint)
>
>or you can swap the order of the first two guarded returns, if
>you like, since their guards are mutually exclusive of course:
>
>def toint(foo):
> if foo>=0: return int(foo&sys.maxint)
> elif foo==-sys.maxint-1: return int(foo)
> else: return -int(-foo&sys.maxint)
>
>the second one is probably going to be marginally faster in
>practical use (fewer calls with -sys.maxint-1 than with >=0
>arguments), and another tiny performance enhancement is:
>
>def toint(foo, themask=sys.maxint, anomaly=-sys.maxint-1):
> if foo>=0: return int(foo&themask)
> elif foo==anomaly: return int(foo)
> else: return -int(-foo&themask)
>
>(probably too tiny to measure in most applications).
>
UIAM there's still a bug. I would model C behavior with
def toint(foo, themask=sys.maxint, signbit=long(sys.maxint)+1):
return int(-(foo&signbit)) + int(foo&themask)
I.e., just mask off sufficient least significant bits of
the wider to fit in the narrower, including the sign bit,
and let the resulting sign bit be interpreted as a normal
sign bit irrespective of the further-up sign bit of the wider.
Note the difference for any positive long with a bit
in the int signbit position (ordinarily bit 31). Indeed,
just the positive long value of 1L<<31 itself will do it.
E.g., using
====< toint.py >================================
import sys
def toint_a(foo, themask=sys.maxint, anomaly=-sys.maxint-1):
if foo>=0: return int(foo&themask)
elif foo==anomaly: return int(foo)
else: return -int(-foo&themask)
def toint_b(foo, themask=sys.maxint, signbit=long(sys.maxint)+1):
return int(-(foo&signbit)) + int(foo&themask)
================================================
>>> import sys
>>> import toint
>>> signbit = long(sys.maxint)+1
>>> signbit
2147483648L
>>> hex(signbit)
'0x80000000L'
>>> toint.toint_a(signbit)
0
>>> toint.toint_b(signbit)
-2147483648
>>> toint.toint_a(+1+signbit)
1
>>> toint.toint_b(+1+signbit)
-2147483647
Or as first mentioned,
>>> toint.toint_a(1L<<31)
0
>>> toint.toint_b(1L<<31)
-2147483648
Regards,
Bengt Richter
More information about the Python-list
mailing list