2's complement conversion. Is this right?

Ivan Illarionov ivan.illarionov at gmail.com
Sat Apr 19 07:35:12 CEST 2008


On Sat, 19 Apr 2008 04:45:54 +0000, Ivan Illarionov wrote:

> On Fri, 18 Apr 2008 22:30:45 -0500, Grant Edwards wrote:
> 
>> On 2008-04-18, Bob Greschke <bob at passcal.nmt.edu> wrote:
>> 
>>> 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).  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 didn't know speed was important.  This might be a little faster
>> (depending on hardware):
>> 
>>   Value = (ord(Buf[s])<<16) | (ord(Buf[s+1])<<8) | ord(Buf[s+2])
>> 
>> It also makes the intention a bit more obvious (at least to me).
>>   
>> A decent C compiler will recognize that <<16 and <<8 are special and
>> just move bytes around rather than actually doing shifts.  I doubt the
>> Python compiler does optimizations like that, but shifts are still
>> usually faster than multiplies (though, again, a good compiler will
>> recognize that multiplying by 65536 is the same as shifting by 16 and
>> just move bytes around).
> 
> So why not put it in C extension?
> 
> It's easier than most people think:
> 
> <code>
> from3bytes.c
> ============
> #include <Python.h>
> 
> PyObject*
> from3bytes(PyObject* self, PyObject* args) {
>     const char * s;
>     int len;
>     if (!PyArg_ParseTuple(args, "s#", &s, &len))
>         return NULL;
>     long n = (s[0]<<16) | (s[1]<<8) | s[2]; if (n >= 0x800000)
>         n -= 0x1000000;
>     return PyInt_FromLong(n);
> }
> 
> static PyMethodDef functions[] = {
>     {"from3bytes",    (PyCFunction)from3bytes, METH_VARARGS}, {NULL,
>     NULL, 0, NULL},
> };
> 
> 
> DL_EXPORT(void)
> init_from3bytes(void)
> {
>     Py_InitModule("_from3bytes", functions);
> }
> 
> buildme.py
> ==========
> import os
> import sys
> from distutils.core import Extension, setup
> 
> os.chdir(os.path.dirname(os.path.abspath(__file__))) sys.argv =
> [sys.argv[0], 'build_ext', '-i'] setup(ext_modules =
> [Extension('_from3bytes', ['from3bytes.c'])]) </code>
> 
> 'python buildme.py' will create '_from3bytes.so' file 'from _from3bytes
> import from3bytes' will import C-optimized function
> 
> Hope this helps.

Sorry,
the right code should be:

PyObject* 
from3bytes(PyObject* self, PyObject* args)
{
    const char * s;
    int len;
    if (!PyArg_ParseTuple(args, "s#", &s, &len))
        return NULL;
    
    long n = ((((unsigned char)s[0])<<16) | (((unsigned char)s[1])<<8) |
((unsigned char)s[2]));
    if (n >= 0x800000)
        n -= 0x1000000;
    return PyInt_FromLong(n);
}



More information about the Python-list mailing list