Need elegant way to cast four bytes into a long

  • Thread starter William S. Huizinga
  • Start date
W

William S. Huizinga

I've got an array.array of unsigned char and would like to make a slice
of that array (e.g. a[0:4]) become one long like I would in "C" :

l = ((unsigned long *) (&a[0]))[0];

I have been getting what I want in this sort of manner :

l = 0L
l = a[0]
l += a[1] << 8
l += a[2] << 16
l += a[3] << 24

but I think that's too wordy. Is there a more intrinsic and elegant way
to do this?
 
T

Terry Reedy

William S. Huizinga said:
I've got an array.array of unsigned char and would like to make a slice
of that array (e.g. a[0:4]) become one long like I would in "C" :

l = ((unsigned long *) (&a[0]))[0];

I have been getting what I want in this sort of manner :

l = 0L
l = a[0]
l += a[1] << 8
l += a[2] << 16
l += a[3] << 24

but I think that's too wordy. Is there a more intrinsic and elegant way
to do this?

el = 0L+ a[0] + (a[1]<< 8) + (a[2]<<16) + (a[3] << 24)

is more compact and must be slightly faster (but parens are needed)

TJR
 
G

Grant Edwards

I've got an array.array of unsigned char and would like to make a slice
of that array (e.g. a[0:4]) become one long like I would in "C" :

l = ((unsigned long *) (&a[0]))[0];

Bad C programming. Your program isn't portable.
I have been getting what I want in this sort of manner :

l = 0L
l = a[0]
l += a[1] << 8
l += a[2] << 16
l += a[3] << 24

but I think that's too wordy. Is there a more intrinsic and elegant way
to do this?

How about this:

l = a[0] + (a[1]<<8) + (a[2]<<16) + (a[3]<<24)

Or you can use struct:

l = struct.unpack("<I","".join([chr(b)for b in a]))[0]

I think that the former is more obvious.
 
S

Skip Montanaro

wsh> I have been getting what I want in this sort of manner :

wsh> l = 0L
wsh> l = a[0]
wsh> l += a[1] << 8
wsh> l += a[2] << 16
wsh> l += a[3] << 24

wsh> but I think that's too wordy. Is there a more intrinsic and
wsh> elegant way to do this?

You mean like this:

l = long(a[0] + a[1] << 8 + a[2] << 16 + a[3] << 24)

You can use struct.unpack as well, though I'd be hard-pressed to get the
details correct. You'd be better off with "pydoc struct".

Skip
 
G

Grant Edwards

I've got an array.array of unsigned char and would like to make a slice
of that array (e.g. a[0:4]) become one long like I would in "C" :

l = ((unsigned long *) (&a[0]))[0];

Bad C programming. Your program isn't portable.

Aside from the endian issue, it may even cause a bus fault or
completely bogus value on many architectures (ARM, SPARC,
etc.). It's actually quite difficult to duplicate that sort of
behavior in Python. ;)
 
A

Alex Martelli

William said:
I've got an array.array of unsigned char and would like to make a slice
of that array (e.g. a[0:4]) become one long like I would in "C" :

l = ((unsigned long *) (&a[0]))[0];

What about:

b = array.array('L')
b.fromstring(a[0:4].tostring())
l = b[0]

I think this should faithfully reproduce the endianness-related
unportability of that C fragment (cannot necessarily reproduce
crashes due to possible use of misaligned pointers, though:).

Actually, I believe you do not really need the .tostring call
in modern Python -- just b.fromstring(a[0:4]) should be
equivalent (and faster). Better, if you need to transform
into longs _many_ adjacent segments of 4 bytes each, this
approach does generalize, and speed is good. Finally, you
can remedy endianness issue with the .byteswap method...



Alex
 
B

Bengt Richter

wsh> I have been getting what I want in this sort of manner :

wsh> l = 0L
wsh> l = a[0]
wsh> l += a[1] << 8
wsh> l += a[2] << 16
wsh> l += a[3] << 24

wsh> but I think that's too wordy. Is there a more intrinsic and
wsh> elegant way to do this?

You mean like this:

l = long(a[0] + a[1] << 8 + a[2] << 16 + a[3] << 24)

You can use struct.unpack as well, though I'd be hard-pressed to get the
details correct. You'd be better off with "pydoc struct".

Skip
This won't be fast, but you get to play with 2.3 ;-)
>>> a = '\x01\x02\x03\x04'
>>> sum([ord(c)<<8*i for i,c in enumerate(a)]) 67305985
>>> hex(sum([ord(c)<<8*i for i,c in enumerate(a)]))
'0x4030201'

or big-endian:
>>> a = '\x01\x02\x03\x04'
>>> sum([ord(c)<<8*i for i,c in enumerate( a[::-1])]) 16909060
>>> hex(sum([ord(c)<<8*i for i,c in enumerate( a[::-1])]))
'0x1020304'


Regards,
Bengt Richter
 
J

John Machin

Skip Montanaro said:
wsh> I have been getting what I want in this sort of manner :

wsh> l = 0L

The above line is redundant.
wsh> l = a[0]
wsh> l += a[1] << 8
wsh> l += a[2] << 16
wsh> l += a[3] << 24

wsh> but I think that's too wordy. Is there a more intrinsic and
wsh> elegant way to do this?

You mean like this:

l = long(a[0] + a[1] << 8 + a[2] << 16 + a[3] << 24)
Bzzzzzt. Oh the joys of operator precedence!

Python 2.2.3 (#42, May 30 2003, 18:12:08) [MSC 32 bit (Intel)] on win32
a = range(0x11,0x55,0x11)
hex(a[0] + a[1] << 8 + a[2] << 16 + a[3] << 24) '0x0'
hex(a[0] + (a[1] << 8) + (a[2] << 16) + (a[3] << 24)) '0x44332211'
hex(a[0] | a[1] << 8 | a[2] << 16 | a[3] << 24)
'0x44332211'

See http://www.python.org/doc/current/ref/summary.html
 
S

Skip Montanaro

>> You mean like this:
>>
>> l = long(a[0] + a[1] << 8 + a[2] << 16 + a[3] << 24)
>>
John> Bzzzzzt. Oh the joys of operator precedence!

Yeah, I realized that after seeing a couple other responses. Should have
kept my mouth shut. I tend to think of << and >> as X2 and /2 operators and
thus mentally lump them together with * / and %. Fortunately, I don't do
much bit twiddling or I'd be in real trouble...

Skip
 
D

Dan Bishop

Skip Montanaro said:
You mean like this:

l = long(a[0] + a[1] << 8 + a[2] << 16 + a[3] << 24)
John> Bzzzzzt. Oh the joys of operator precedence!

Yeah, I realized that after seeing a couple other responses. Should have
kept my mouth shut. I tend to think of << and >> as X2 and /2 operators and
thus mentally lump them together with * / and %. Fortunately, I don't do
much bit twiddling or I'd be in real trouble...

One correct way of writing the expression without parentheses is

a[0] | a[1] << 8 | a[2] << 16 | a[3] << 24
 
M

Max M

William said:
I've got an array.array of unsigned char and would like to make a slice
of that array (e.g. a[0:4]) become one long like I would in "C" :

l = ((unsigned long *) (&a[0]))[0];

I have been getting what I want in this sort of manner :

l = 0L
l = a[0]
l += a[1] << 8
l += a[2] << 16
l += a[3] << 24

but I think that's too wordy. Is there a more intrinsic and elegant way
to do this?


def readBew(value):
"Reads string as big endian word, (asserts len(string) in [1,2,4])"
return unpack('>%s' % {1:'b', 2:'H', 4:'l'}[len(value)], value)[0]

def writeBew(value, length):
"""
Write int as big endian formatted string,
(asserts length in [1,2,4])
"""
return pack('>%s' % {1:'b', 2:'H', 4:'l'}[length], value)

Any of those usefull?

regards Max M
 
A

Anton Vredegoor

William S. Huizinga said:
I've got an array.array of unsigned char and would like to make a slice
of that array (e.g. a[0:4]) become one long like I would in "C" :

l = ((unsigned long *) (&a[0]))[0];

I have been getting what I want in this sort of manner :

l = 0L
l = a[0]
l += a[1] << 8
l += a[2] << 16
l += a[3] << 24

but I think that's too wordy. Is there a more intrinsic and elegant way
to do this?

Just for completeness, it's also possible to go back to basic style
programming.

Anton

from string import hexdigits

def hexchr(i): return hexdigits[i/16]+hexdigits[i%16]
asc = dict([(chr(i), hexchr(i)) for i in range(256)])
def tolong(s): return long(''.join(map(asc.get,s[::-1])),16)

def test():
a = '\x01\x02\x03\x04'
print tolong(a)

if __name__=='__main__':
test()
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top