2's complement conversion. Is this right?

George Sakkis george.sakkis at gmail.com
Fri Apr 18 18:04:37 EDT 2008


On Apr 18, 5:26 pm, Bob Greschke <b... at passcal.nmt.edu> wrote:

> On 2008-04-18 14:37:21 -0600, Ross Ridge
> <rri... at caffeine.csclub.uwaterloo.ca> said:
>
>
>
> > Bob Greschke  <b... at passcal.nmt.edu> wrote:
> >> I'm reading 3-byte numbers from a file and they are signed (+8 to
> >> -8million).  This seems to work, but I'm not sure it's right.
>
> >> # Convert the 3-characters into a number.
> >> Value1, Value2, Value3 = unpack(">BBB", Buffer[s:s+3])
> >> Value = (Value1*65536)+(Value2*256)+Value3
> >> if Value >= 0x800000:
> >> Value -= 0x1000000
> >> print Value
>
> >> For example:
> >> 16682720 = -94496
>
> >> Should it be Value -= 0x1000001 so that I get -94497, instead?
>
> > Your first case is correct, "Value -= 0x1000000".  The value 0xFFFFFFF
> > should be -1 and 0xFFFFFFF - 0x1000000 == -1.
>
> > An alternative way of doing this:
>
> >    Value = unpack(">l", Buffer[s:s+3] + "\0")[0] >> 8
>
> >                                    Ross Ridge
>
> Good to know (never was good on the math front).
>
> However, in playing around with your suggestion and Grant's code I've
> found that the struct stuff is WAY slower than doing something like this
>
>  Value = (ord(Buf[s])*65536)+(ord(Buf[s+1])*256)+ord(Buf[s+2])
>  if Value >= 0x800000:
>      Value -= 0x1000000
>
> This is almost twice as fast just sitting here grinding through a few
> hundred thousand conversions (like 3sec vs. ~5secs just counting on my
> fingers - on an old Sun...it's a bit slow).  

You'd better use a more precise timing method than finger counting,
such as timeit. Twice as fast is probably a gross overestimation; on
my box (Python 2.5, WinXP) avoiding unpack is around 10% and 40%
faster from Ross's and Grant's method, respectively:

python -m timeit "for i in xrange(1000):from3Bytes_bob(s)" \
       -s "from bin import *; s=pack('>i',1234567)[1:]"
1000 loops, best of 3: 1.02 msec per loop

python -m timeit "for i in xrange(1000):from3Bytes_grant(s)" \
       -s "from bin import *; s=pack('>i',1234567)[1:]"
1000 loops, best of 3: 1.43 msec per loop

python -m timeit "for i in xrange(1000):from3Bytes_ross(s)" \
       -s "from bin import *; s=pack('>i',1234567)[1:]"
1000 loops, best of 3: 1.12 msec per loop

### bin.py ##########################################

from struct import unpack

def from3Bytes_bob(s):
    Value = (ord(s[0])<<16) + (ord(s[1])<<8) + ord(s[2])
    if Value >= 0x800000:
        Value -= 0x1000000
    return Value

def from3Bytes_grant(s):
    if ord(s[0]) & 0x80:
        s = '\xff'+s
    else:
        s = '\x00'+s
    return unpack('>i',s)[0]

def from3Bytes_ross(s):
    return unpack(">l", s + "\0")[0] >> 8


> Replacing *65536 with <<16
> and *256 with <<8 might even be a little faster, but it's too close to
> call without really profiling it.

> I wasn't planning on making this discovery today! :)
>
> Bob

If you are running this on a 32-bit architecture, get Psyco [1] and
add at the top of your module:
    import psyco; psyco.full()

Using Psyco in this scenatio is up to 70% faster:

python -m timeit "for i in xrange(1000):from3Bytes_bob(s)" \
       -s "from bin import *; s=pack('>i',1234567)[1:]"
1000 loops, best of 3: 624 usec per loop

python -m timeit "for i in xrange(1000):from3Bytes_grant(s)" \
       -s "from bin import *; s=pack('>i',1234567)[1:]"
1000 loops, best of 3: 838 usec per loop

python -m timeit "for i in xrange(1000):from3Bytes_ross(s)" \
       -s "from bin import *; s=pack('>i',1234567)[1:]"
1000 loops, best of 3: 834 usec per loop


George

[1] http://psyco.sourceforge.net/



More information about the Python-list mailing list