Convert hex to float

后端 未结 5 1241
清歌不尽
清歌不尽 2020-11-27 06:07

How to convert the following hex string to float (single precision 32-bit) in Python?

\"41973333\" -> 1.88999996185302734375E1

\"41995C29\" -> 1.91700         


        
相关标签:
5条回答
  • 2020-11-27 06:36

    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
    
    0 讨论(0)
  • 2020-11-27 06:38

    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. :)

    0 讨论(0)
  • 2020-11-27 06:43

    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.

    0 讨论(0)
  • 2020-11-27 06:52

    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
    
    0 讨论(0)
  • 2020-11-27 06:53

    I'm guessing this question relates to this one and you are working with 4 bytes rather than 8 hex digits.

    "\x41\x91\x33\x33" is a 4 byte string even though it looks like 16

    >>> len("\x41\x91\x33\x33")
    4
    >>> import struct  
    >>> struct.unpack(">fff","\x41\x97\x33\x33\x41\x99\x5C\x29\x47\x0F\xC6\x14")
    (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
    
    0 讨论(0)
提交回复
热议问题