How to convert the following hex string to float (single precision 32-bit) in Python?
"41973333" -> 1.88999996185302734375E1 "41995C29" -> 1.91700000762939453125E1 "470FC614" -> 3.6806078125E4
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
In Python 3:
>>> import struct
>>> struct.unpack('!f', bytes.fromhex('41973333'))[0]
18.899999618530273
>>> struct.unpack('!f', bytes.fromhex('41995C29'))[0]
19.170000076293945
>>> struct.unpack('!f', bytes.fromhex('470FC614'))[0]
36806.078125
In Python 2:
>>> import struct
>>> struct.unpack('!f', '41973333'.decode('hex'))[0]
18.899999618530273
>>> struct.unpack('!f', '41995C29'.decode('hex'))[0]
19.170000076293945
>>> struct.unpack('!f', '470FC614'.decode('hex'))[0]
36806.078125
Method 2
I recommend using the ctypes module which basically lets you work with low level data types. In your case you could say
from ctypes import *
def convert(s):
i = int(s, 16) # convert from hex to a Python int
cp = pointer(c_int(i)) # make this into a c integer
fp = cast(cp, POINTER(c_float)) # cast the int pointer to a float pointer
return fp.contents.value # dereference the pointer, get the float
print convert("41973333") # returns 1.88999996185302734375E1
print convert("41995C29") # returns 1.91700000762939453125E1
print convert("470FC614") # returns 3.6806078125E4
I believe that the ctypes module makes sense here, because you’re essentially asking how to perform low-level bit casting. Your question is basically, how do I tell Python to take some data and interpret that data as if those exact same bits were a different data type?
In C if you had an int and wanted to interpret its bits as a float, you’d do roughly the same thing, taking a pointer and then casting and dereferencing it:
int i = 0x41973333; float f = *((float*)&i);
and that’s exactly what the Python code using the ctypes library is doing in my example.
Method 3
I’m guessing this question relates to this one and you are working with 4 bytes rather than 8 hex digits.
"x41x91x33x33" is a 4 byte string even though it looks like 16
>>> len("x41x91x33x33")
4
>>> import struct
>>> struct.unpack(">fff","x41x97x33x33x41x99x5Cx29x47x0FxC6x14")
(18.899999618530273, 19.170000076293945, 36806.078125)
If you do need to deal with the string of hexdigits rather than the actual bytes, you can use struct.pack to convert it, like this
>>> for hx in ["41973333","41995C29","470FC614"]:
... print(struct.unpack(">f",struct.pack(">i",int(hx,16)))[0])
...
18.8999996185
19.1700000763
36806.078125
Method 4
Slice up the hex strings into 2-character chunks (bytes), make each chunk into the right byte with int formatting, struct.unpack when done. I.e.:
import struct
testcases = {
"41973333": 1.88999996185302734375E1,
"41995C29": 1.91700000762939453125E1,
"470FC614": 3.6806078125E4,
}
def hex2float(s):
bins = ''.join(chr(int(s[x:x+2], 16)) for x in range(0, len(s), 2))
return struct.unpack('>f', bins)[0]
for s in testcases:
print hex2float(s), testcases[s]
emitting, as desired:
18.8999996185 18.8999996185 19.1700000763 19.1700000763 36806.078125 36806.078125
Method 5
When working from string and without having to slice or index values.
import struct
...
q = int('0x425c0000',16)
b8 = struct.pack('i', q)
dec, = struct.unpack('f', b8)
Method 6
Gentelmen… Behold:
class fl:
def __init__(this, value=0, byte_size=4):
this.value = value
if this.value: # speedy check (before performing any calculations)
Fe=((byte_size*8)-1)//(byte_size+1)+(byte_size>2)*byte_size//2+(byte_size==3)
Fm,Fb,Fie=(((byte_size*8)-(1+Fe)), ~(~0<<Fe-1), (1<<Fe)-1)
FS,FE,FM=((this.value>>((byte_size*8)-1))&1,(this.value>>Fm)&Fie,this.value&~(~0 << Fm))
if FE == Fie: this.value=(float('NaN') if FM!=0 else (float('+inf') if FS else float('-inf')))
else: this.value=((pow(-1,FS)*(2**(FE-Fb-Fm)*((1<<Fm)+FM))) if FE else pow(-1,FS)*(2**(1-Fb-Fm)*FM))
del Fe; del Fm; del Fb; del Fie; del FS; del FE; del FM
else: this.value = 0.0
print fl( 0x41973333 ).value # >>> 18.899999618530273
print fl( 0x41995C29 ).value # >>> 19.170000076293945
print fl( 0x470FC614 ).value # >>> 36806.078125
print fl( 0x00800000 ).value # >>> 1.1754943508222875e-38 (minimum float value)
print fl( 0x7F7FFFFF ).value # >>> 340282346638528859811704183484516925440L (maximum float value)
# looks like I've found a small bug o.o
# the code still works though (the numbers are properly formatted)
# the result SHOULD be: 3.4028234663852886e+38 (rounded)
print fl( 0x3f80000000, 5 ).value # >>> 1.0
sorry for the little “.value” at the end…
this code has been used as a class in my program for nearly 2 years now.
(with a little editing, you can easily make it into a function)
credit to PyTony over at DaniWeb for the code.
unlike non-dynamic computing,
the code is not hard-wired to a fixed float size,
and works with any byte-size.
though I guess we still have a few bugs to work out. XDD
(I’ll edit this code later (if I can) with the update)
all is good though for now though…
I havn’t had a problem converting 3D game models with it. 🙂
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0