【问题标题】:ctypes cast() returning nan when casting an int pointer to a float pointerctypes cast() 在将 int 指针转换为浮点指针时返回 nan
【发布时间】:2021-08-17 16:35:33
【问题描述】:

我正在读取二进制文件,需要将数据解释为一堆不同的数据类型。 我正在混合使用 numpy 和 ctypes。

我需要做的转换之一是从 4 个 uint16 值到 IEEE 754 双精度值。

我正在像这样组合 uint:

   fval = np.uint64((np.int64(uint16_arr[0]) << 48) | (np.int64(uint16_arr[1]) << 32)) | (uint16_arr[2] << 16) | (uint16_arr[3])

并使用我在另一个答案中找到的一些代码将它们转换为双精度:

   fval = convert_64(fval)

def convert_64(s): # converts doubles
    cp = pointer(c_uint64(s)) # make this into a 64 bit c integer
    fp = cast(cp, POINTER(c_double)) # cast the int pointer to a float pointer
    return fp.contents.value # dereference the pointer, get the float
    # function from here: https://stackoverflow.com/questions/1592158/convert-hex-to-float

这适用于除一组数据之外的所有数据:[C0 84] [84 B6] [B8 F9] [B1 31]

由于某种原因 cast() 为此返回 nan。我已经通过打印确认该值一直到 cp 变量,但由于某种原因 cast() 无法将其传递到 fp 变量。 我需要转换为双精度的所有其他数据都可以很好地通过并得出正确的值。就是这一套给我带来了问题。

任何帮助或建议将不胜感激。

【问题讨论】:

  • 您的样本数据的预期结果是什么? struct.unpack('&gt;d',bytes.fromhex('C0 84 84 B6 B8 F9 B1 31'))[0] -> -656.58922,

标签: python numpy casting double ctypes


【解决方案1】:

注意事项

  • 正如在 cmets 中看到的那样,您可能想要使用 [Python.Docs]: struct - Interpret strings as packed binary data,因为它更简单。 CTypes 需要一些指针知识
  • 不知道你想在这里拉什么,但是将 uint64 重新解释为 double 似乎不是一个好主意(因为表示方式不同),一个完全有效的 uint64 可能会产生 NaN
  • 无法重现您的行为
    • 除了 @Base 注释的示例之外,所有示例都是为了测试各种字节序变体

code00.py

#!/usr/bin/env python

import sys
import ctypes as ct


def ui2d(ui):
    #cp = ct.pointer(ct.c_uint64(ui))  # Same thing in 2 steps
    #return ct.cast(cp, ct.POINTER(ct.c_double)).contents.value
    return ct.cast(ct.pointer(ct.c_uint64(ui)), ct.POINTER(ct.c_double)).contents.value


def print_ui(ui):
    print("0x{:016X} {:d} {:.6f} {:.6f}".format(ui, ui, ui, ui2d(ui)))


def main(*argv):
    s0 = "C08484B6B8F9B131"
    s1 = "84C0B684F9B831B1"
    print_ui(int(s0, 16))  # @Base
    print_ui(int(s0[::-1], 16))  # Reverse
    print_ui(int(s1, 16))



if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

输出

[cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q068821063]> ${PY_VENVS_HOME}/py_pc064_03_08_test0/bin/python code00.py 
Python 3.8.10 (default, Jun  2 2021, 10:49:15) [GCC 9.4.0] 064bit on linux

0xC08484B6B8F9B131 13872358672530256177 13872358672530255872.000000 -656.589220
0x131B9F8B6B48480C 1376869532240988172 1376869532240988160.000000 0.000000
0x84C0B684F9B831B1 9565846290776469937 9565846290776469504.000000 -0.000000

【讨论】:

  • 感谢您的回复!我同意将数据重新解释为双重可能会出现问题。然而,它的测试数据应该始终是双精度的,只是在一个包含一堆不同数据类型的二进制文件中,我必须对其进行整理。我终于找到了问题所在。这是我正在做的位移来创建我传入的数字。我不知道为什么,但是当我更改我的代码以将 4 个 unit16s 组合为十六进制字符串,然后将十六进制字符串转换为整数时,它奏效了。
  • 如果它是双精度的,那么就这样阅读(避免转换)。无论如何:如果是,这回答了您的问题,那么请接受答案(将其标记为解决方案),以便其他人也能够承认它([SO]: What should I do when someone answers my question?)。如果没有,请告诉我如何改进它,以便它确实回答它。
【解决方案2】:

我发现了问题!这是我组合 uint16s 的方式。 当我将其作为连接的十六进制值字符串进行时,一切正常。 我替换了这一行:

fval = np.uint64((np.int64(uint16_arr[0]) << 48) | (np.int64(uint16_arr[1]) << 32)) | (uint16_arr[2] << 16) | (uint16_arr[3])

用这个:

fval = "{MSB:04X}{MID_HI:04X}{MID_LOW:04X}{LSB:04X}".format(MSB = uint16_arr[0],MID_HI = uint16_arr[1],MID_LOW = uint16_arr[2],LSB = uint16_arr[3])

然后我将这个添加到我的转换函数的开头:

 def convert_64(s): # converts doubles
    i = int(s, 16)  # convert from hex to a python int
    cp = pointer(c_uint64(i)) # make this into a 64 bit c integer

这使它适用于我的问题数据集。我不知道为什么移位不起作用,但这是问题所在。 感谢大家的帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-12
    • 2012-09-06
    • 1970-01-01
    • 2021-07-26
    • 2012-03-02
    相关资源
    最近更新 更多