# 2's complement conversion. Is this right?

Bob Greschke bob at passcal.nmt.edu
Sat Apr 19 23:24:32 CEST 2008

```On 2008-04-18 23:35:12 -0600, Ivan Illarionov <ivan.illarionov at gmail.com> said:

> 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);
> }

No thanks.  Being able to alter and install these programs on whatever
computer they are installed on is more important than speed.  I went
down the C-extension path years ago and it turned into a big mess.
Everything has to run on Sun's, Mac's, Linux and Windows without any
major hassels if they have to be changed.  I can't reley on everything
the program needs to be rebuilt being installed beyond Python and
Tkinter (and pySerial and PIL for a couple of programs), which they
need to run.  So no compiling and everything is in one file, in case a
new version has to be upgraded by a user by emailing it to them while
they're sitting on some mountain in Tibet.  Just unzip, stick the .py
somewhere logical, (usually) double-click, and they are off and running.

Bob

```