如何将整数解释为浮点数

5

我正在尝试使用用C#编写的DLL与激光通信。我已经成功地使用ctypes模块加载了DLL函数。我想要使用的函数声明如下:

LONG LJV7IF_GetStorageData( LONG lDeviceId, 
                            LJV7IF_GET_STORAGE_REQ* pReq,
                            LJV7IF_STORAGE_INFO* pStorageInfo,
                            LJV7IF_GET_STORAGE_RSP* pRsp, 
                            DWORD* pdwData, 
                            DWORD dwDataSize );

我希望通过dword指针pdwData访问数据。激光器通过以下结构发送其存储的数据:

Bytes |  Meaning  |   Types
 0-3  |   time    |    dword          
  4   |  judgment |    byte
  5   | meas info |    byte
 ...  |    ...    |    ... 
 8-11 |   data    |    float

这是我使用该函数的方式:
self.dll = WinDLL( "LJV7_IF.dll" )
self._getStoredData = self.dll.LJV7IF_GetStorageData
self._getStoredData.restype = c_int32
self._getStoredData.argstypes = [ c_int32,
                                  POINTER( GET_STORAGE_REQ ),
                                  POINTER( STORAGE_INFO ),
                                  POINTER( GET_STORAGE_RSP ),
                                  POINTER( c_uint32 ),
                                  c_uint32 ]
dataSize = 132
dataBuffer = c_uint32 * ( dataSize / 4 )
outputData_p = POINTER( c_uint32 )( dataBuffer() )
self._getStoredData( deviceID,
                     byref( myStruct ),
                     byref( storageInfo ),
                     byref( storageResponse ),
                     outputData_p ),
                     dataSize )

为了简洁起见,myStruct、storageInfo和storageResponse没有详细介绍(它们在其他DLL函数调用中使用,似乎运作正常)。

我的问题是:当我尝试访问outputData_p[ 2 ]时,Python返回给我一个整数值,比如1066192077。这正是我所要求的。但是我希望将该整数解释/转换为浮点数,这个浮点数应该是1.1或类似的值(无法记住确切的值)。将其强制转换为浮点数不起作用(我得到了1066192077.00),使用hex() -> bytes() -> struct.unpack( )也不行。我该怎么办?


你尝试过将其打包然后解压为浮点数吗? - Niklas B.
1
你能解释一下你期望的浮点数是什么类型吗?DWORD只是一个无符号32位整数,你要查找什么类型的浮点数据呢?它是否被缩放了?如果是,比例是多少? - Avantol13
@eryksun,如果我理解正确,我应该将outputData_p替换为dataBuffer = (c_uint32 * 33 )()并传递给我的函数,以遵守这个错误,一旦我的函数返回,我将把我的_int_缓冲区转换为_float_缓冲区,使用floatBuffer = (c_float * 33).from_buffer(dataBuffer) - Nessy W.
我编写了argstypes表达式(这样我可以修改它),以遵循C#代码(我无法更改)。我之所以这样做是因为我认为如果我直接传递浮点数数组而它期望的是无符号整数数组,那么dll会抛出错误...并且读取返回结构的第一个双字(时间轴)的问题也将是相同的。 - Nessy W.
@eryksun,您的两个解决方案在我的情况下都有效。所以我认为您可以将其发布为答案,我会接受它作为解决方案。您能否详细说明为什么它有效以及为什么应该这样做?我不确定我真正理解了argstypes在这个混乱中的作用。 - Nessy W.
1个回答

5

注意:如果您在搜索如何将整数转换为单精度浮点数时到达此处,则忽略本答案的其余部分,只需使用J.F. Sebastian评论中的代码即可。它使用struct模块而不是ctypes,更简单且始终可用,而ctypes是Python标准库中可选包:

import struct

def float_from_integer(integer):
    return struct.unpack('!f', struct.pack('!I', integer))[0]

assert float_from_integer(1066192077) == 1.100000023841858

使用ctypes的from_buffer方法,您可以将数组解释为不同类型。一般来说,您可以将具有可写缓冲区接口的任何对象传递给此方法,而不仅仅是ctypes实例,例如bytearray或NumPy数组。

例如:

>>> from ctypes import *
>>> int_array = (c_int * 4)(1, 2, 3, 4)
>>> n_doubles = sizeof(int_array) // sizeof(c_double)
>>> array_t = c_double * n_doubles
>>> double_array = array_t.from_buffer(int_array)

这仍然是同样的字节,只是重新解释为两个8字节的双精度浮点数:

>>> bytes(double_array)
b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00'
>>> double_array[:]
[4.2439915824e-314, 8.4879831653e-314]

由于这个数组是通过调用from_buffer创建的,而不是from_buffer_copy,因此它实际上是一个视图,与原始数组共享同一缓冲区。例如,如果将最后2个整数移到前面,则double_array中的值也会被交换:

>>> int_array[:] = [3, 4, 1, 2]
>>> double_array[:]
[8.4879831653e-314, 4.2439915824e-314]

请注意,您的示例代码中有一个拼写错误。定义函数参数类型的属性名称是argtypes,而不是argstypes
self._getStoredData.argstypes = [ c_int32,
                    ^^^^^^^^^

定义这个原型不是必需的,但建议这样做。这使得ctypes在每次调用函数时调用相应的from_param方法来处理每个参数。如果没有原型,缺省的参数处理接受ctypes实例并自动将字符串转换为char *wchar_t *,并将整数转换为C中的int值;否则会引发ArgumentError
您可以按照以下方式定义打包(即无对齐填充)的数据记录:
class LaserData(Structure):
    _pack_ = 1
    _fields_ = (('time',      c_uint),
                ('judgement', c_byte),
                ('meas_info', c_byte * 3),
                ('data',      c_float))

这里是一个示例类,将通用数据参数类型定义为POINTER(c_byte),并根据设备实例确定结果为记录数组。显然,这只是类应该如何定义的要点,因为我对此 API 几乎一无所知。

class LJV7IF(object):
    # loading the DLL and defining prototypes should be done
    # only once, so we do this in the class (or module) definition.
    _dll = WinDLL("LJV7_IF")
    _dll.LJV7IF_Initialize()

    _dll.LJV7IF_GetStorageData.restype = c_long
    _dll.LJV7IF_GetStorageData.argtypes = (c_long,
                                           POINTER(GET_STORAGE_REQ),
                                           POINTER(STORAGE_INFO),
                                           POINTER(GET_STORAGE_RSP),
                                           POINTER(c_byte),
                                           c_uint)

    def __init__(self, device_id, record_type):
        self.device_id = device_id
        self.record_type = record_type

    def get_storage_data(self, count):
        storage_req = GET_STORAGE_REQ()
        storage_info = STORAGE_INFO()
        storage_rsp = GET_STORAGE_RSP()
        data_size = sizeof(self.record_type) * count
        data = (c_byte * data_size)()
        result = self._dll.LJV7IF_GetStorageData(self.device_id,
                                                 byref(storage_req),
                                                 byref(storage_info),
                                                 byref(storage_rsp),
                                                 data,
                                                 data_size)
        if result < 0: # assume negative means an error.
            raise DeviceError(self.device_id) # an Exception subclass.
        return (self.record_type * count).from_buffer(data)

例如:

if __name__ == '__main__':
    laser = LJV7IF(LASER_DEVICE_ID, LaserData)
    for record in laser.get_storage_data(11):
        print(record.data)

好的,这值得大声感谢!直到你让我意识到argtype中的额外s,我才明白我的Python调用为什么没有进行类型检查!我喜欢那个from_buffer函数。我整天都在寻找这个精确的功能,但不敢在这里问,你只用几分钟就解决了它。_太棒了_。 - Nessy W.
不用谢。我更喜欢 Python 3 中 from_buffer 的新实现方式。我们刚刚重写了它,以消除它对 memoryview 对象的破坏性操作,这是导致问题 25498 崩溃的原因。现在它已经非常可靠了。Python 2 仍然调用旧的 PyObject_AsWriteBuffer,它假定对象永远不会在新地址上重新分配其缓冲区。 - Eryk Sun
例如,在Python 2中,您不能依赖于b = bytearray(b'abc'); c = (c_char* 3).from_buffer(b); print(repr(c[:])); b.extend(b'123' * 10); print(repr(c[:]))。最后一个打印可能是任何内容,甚至可能会导致解释器崩溃。在Python 3中,在将缓冲区导出到memoryview之后尝试使用b.extend会引发BufferError,因为导出是被跟踪的。 - Eryk Sun
1
要回答标题中的问题,您可以提到:struct.unpack('!f', struct.pack('!I', 1066192077))[0] == 1.100000023841858 - jfs
@eryksun: 我知道。这是为了那些通过标题来到这里的谷歌用户而设的。要么更改标题,要么提供明确(而非暗示)问题的答案。 - jfs
@J.F.Sebastian,我为任何想要将整数转换为浮点数的人添加了一条注释。 - Eryk Sun

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接