Integer arithmetic

Bengt Richter bokr at oz.net
Sun Mar 30 01:01:20 CET 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