How to interpret 4 bytes as a 32-bit float using Python - python

I am sort of a novice to the Python language and am having a hard time doing something I could very easily do with C++ or Java but for some reason seems so convoluted to do in Python. I have the following four bytes in an array (in big endian order):
[0x64, 0xD8, 0x6E, 0x3F]
I already know beforehand what these bytes represent. They specify the following 32-bit floating-point number: 0.932989
What are the steps I need to perform using Python (preferably v3.2.1 and without using extra imports) to interpret those 4 bytes as that float and store that number in a variable which I can manipulate as a 32-bit floating-point value? I.e. so i can use it just as the following variable myVar = 0.932989
I've tried:
x = [0x64, 0xd8, 0x6e, 0x3f]
y = int.from_bytes(x, byteorder='little', signed=False) #interpret bytes as an unsigned little-endian integer (so far so good)
z = float(y) #attempt to cast as float reinterprets integer value rather than its byte values
y has the right expected integer interpretation of those bytes, which is 1064228964, the problem comes when casting that to a 32-bit float. Instead of casting the raw bytes of y as a float, it casts the integer representation of those bytes, so z contains 1064228964.0 instead of the desired value of 0.932989. Is there maybe something equivalent to int.from_bytes that I can use to perform this simple task? Perhaps something like float.from_bytes?

For detail see Python Struct. For your specific question:
import struct
# if input is string, per #robyschek will fail on python 3
data=b'\x64\xd8\x64\x3f'
print struct.unpack('<f', data) #little endian
print struct.unpack('>f', data) # big endian
#your input
list1=[0x64, 0xD8, 0x6E, 0x3F]
# aa=str(bytearray(list1)) # edit: this conversion wasn't needed
aa= bytearray(list1)
print struct.unpack('<f', aa)
​
output:
(0.8939268589019775,)
(3.193376169798871e+22,)
(0.9329893589019775,)

If you're willing to use a big library that's really for handling (large) arrays of numbers efficiently:
import numpy as np
data_bytes = np.array([0x64, 0xD8, 0x6E, 0x3F], dtype=np.uint8)
data_as_float = data_bytes.view(dtype=np.float32)
print(data_as_float)
This will also work on big byte arrays; then you get an array of floats.

Related

Python3.4 convert int16_t string to signed float

I apologise if this question has already been answered here. My searches have turned up similar questions but I have been unsuccessful in applying them to my situation.
I receive data via serial communication as two 16bit hexadecimal bytes. Now represented by two python integers. [48822, 15938]
These bytes represent a signed floating point variable. i.e. [48822, 15938]-->[0xbeb6, 0x3e42]-->[0x3e42beb6]-->[0.190181]
How would I complete this conversion in python3.4?
>>> import struct
>>> f = struct.unpack('f',struct.pack('HH', 48822,15938))[0]
>>> f
0.19018062949180603
>>>
You can use *lst to convert your list to parameters for struct.pack.
If you're getting a significant amount of data this way, you may want to consider using numpy. You can pack a buffer of dtype np.uint16 and view the contents directly as np.float32 without copying any data:
n = 1
buffer = np.zeros(2 * n, dtype=np.uint16)
floats = buffer.view(np.float32)
buffer[:2] = [48822, 15938]
print(floats)

Convert double number to float number

I need to do some elaborations on MATLAB data in Python.
The data is stored as an array of doubles in Matlab. When I retrieve it, despite being stated here that double data types from Matlab are converted in float data types when handled by Python, I get this error:
TypeError: unorderable types: double() < float()
What I'm trying to do is this
import matlab.engine
eng=matlab.engine.connect_matlab()
x = eng.workspace['MyData']
x = x[len(x)-1]
if x < 0.01:
#do stuff
How can I convert the double number stored in the array to a float so that I can use it alongside my other Python variables?
Converting doubles into floats in Matlab is as simple as calling the single function:
A = rand(10);
whos A;
B = single(A);
whos B;
As per console output:
Name Size Bytes Class Attributes
A 10x10 800 double
Name Size Bytes Class Attributes
B 10x10 400 single
Be careful about the loss of precision, since you are converting 64 bit numeric values into 32 bit numeric values.
EDIT
Since you can't manipulate your Matlab data, in order to accomplish this I suggest you to use either Numpy (refer to this function in case: https://docs.scipy.org/doc/numpy-1.12.0/reference/generated/numpy.ndarray.astype.html) or struct for a straight conversion (refer to this answer in case: convert double to float in Python).

How to fix python module numpy's representation of integers in as hexadecimal

I want to send out a bytearray using serial module. Elements in the bytearray are float data types that are rounded and converted to integers, for this reason I use numpy for speed efficiency rather than pythons native data type conversion functions. Problem is numpy is incorrectly representing decimal integers 9, 10, and 13 inside bytearray. I'm using python 3.4.2 and numpy 1.9.2 on Windows 7 x64.
Here is example of problem:
import numpy
>>> x = bytearray([])
>>> for i in range(20):
x.append(numpy.int16(i))
>>> x
bytearray(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13')
decimal integers 9, 10, 13 become t, n, and r when they should be represented as 0x09, 0x0a, 0x0d
The values are actually correct. What you are seeing is how the printing which calls repr on the object formats those as bytes as escape sequences. \n is the same as \x0a as 0a is 10. \n is chosen over \x0a as it is shorter and more easily recognized.
Now if you want to print the bytes as hex as in 45 rather than \x45 use the binascii.hexlify function to convert before printing.

Convert a list of ints to a float

I am trying to convert a number stored as a list of ints to a float type. I got the number via a serial console and want to reassemble it back together into a float.
The way I would do it in C is something like this:
bit_data = ((int16_t)byte_array[0] << 8) | byte_array[1];
result = (float)bit_data;
What I tried to use in python is a much more simple conversion:
result = int_list[0]*256.0 + int_list[1]
However, this does not preserve the sign of the result, as the C code does.
What is the right way to do this in python?
UPDATE:
Python version is 2.7.3.
My byte array has a length of 2.
in the python code byte_array is list of ints. I've renamed it to avoid misunderstanding. I can not just use the float() function because it will not preserve the sign of the number.
I'm a bit confused by what data you have, and how it is represented in Python. As I understand it, you have received two unsigned bytes over a serial connection, which are now represented by a list of two python ints. This data represents a big endian 16-bit signed integer, which you want to extract and turn into a float. eg. [0xFF, 0xFE] -> -2 -> -2.0
import array, struct
two_unsigned_bytes = [255, 254] # represented by ints
byte_array = array.array("B", two_unsigned_bytes)
# change above to "b" if the ints represent signed bytes ie. in range -128 to 127
signed_16_bit_int, = struct.unpack(">h", byte_array)
float_result = float(signed_16_bit_int)
I think what you want is the struct module.
Here's a round trip snippet:
import struct
sampleValue = 42.13
somebytes = struct.pack('=f', sampleValue)
print(somebytes)
result = struct.unpack('=f', somebytes)
print(result)
result may be surprising to you. unpack returns a tuple. So to get to the value you can do
result[0]
or modify the result setting line to be
result = struct.unpack('=f', some bytes)[0]
I personally hate that, so use the following instead
result , = struct.unpack('=f', some bytes) # tuple unpacking on assignment
The second thing you'll notice is that the value has extra digits of noise. That's because python's native floating point representation is double.
(This is python3 btw, adjust for using old versions of python as appropriate)
I am not sure I really understand what you are doing, but I think you got 4 bytes from a stream and know them to represent a float32 value. The way you handling this suggests big-endian byte-order.
Python has the struct package (https://docs.python.org/2/library/struct.html) to handle bytestreams.
import struct
stream = struct.pack(">f", 2/3.)
len(stream) # 4
reconstructed_float = struct.unpack(">f", stream)
Okay, so I think int_list isn't really just a list of ints. The ints are constrained to 0-255 and represent bytes that can be built into a signed integer. You then want to turn that into a float. The trick is to set the sign of the first byte properly and then procede much like you did.
float((-(byte_array[0]-127) if byte_array[0]>127 else byte_array[0])*256 + byte_array[1])

Convert decimal int to little endian string ('\x##\x##...')

I want to convert an integer value to a string of hex values, in little endian. For example, 5707435436569584000 would become '\x4a\xe2\x34\x4f\x4a\xe2\x34\x4f'.
All my googlefu is finding for me is hex(..) which gives me '0x4f34e24a4f34e180' which is not what I want.
I could probably manually split up that string and build the one I want but I'm hoping somone can point me to a better option.
You need to use the struct module:
>>> import struct
>>> struct.pack('<Q', 5707435436569584000)
'\x80\xe14OJ\xe24O'
>>> struct.pack('<Q', 5707435436569584202)
'J\xe24OJ\xe24O'
Here < indicates little-endian, and Q that we want to pack a unsigned long long (8 bytes).
Note that Python will use ASCII characters for any byte that falls within the printable ASCII range to represent the resulting bytestring, hence the 14OJ, 24O and J parts of the above result:
>>> struct.pack('<Q', 5707435436569584202).encode('hex')
'4ae2344f4ae2344f'
>>> '\x4a\xe2\x34\x4f\x4a\xe2\x34\x4f'
'J\xe24OJ\xe24O'
I know it is an old thread, but it is still useful. Here my two cents using python3:
hex_string = hex(5707435436569584202) # '0x4f34e24a4f34e180' as you said
bytearray.fromhex(hex_string[2:]).reverse()
So, the key is convert it to a bytearray and reverse it.
In one line:
bytearray.fromhex(hex(5707435436569584202)[2:])[::-1] # bytearray(b'J\xe24OJ\xe24O')
PS: You can treat "bytearray" data like "bytes" and even mix them with b'raw bytes'
Update:
As Will points in coments, you can also manage negative integers:
To make this work with negative integers you need to mask your input with your preferred int type output length. For example, -16 as a little endian uint32_t would be bytearray.fromhex(hex(-16 & (2**32-1))[2:])[::-1], which evaluates to bytearray(b'\xf0\xff\xff\xff')

Categories

Resources