【问题标题】:long double returns and ctypeslong double 返回和 ctypes
【发布时间】:2015-03-12 02:31:49
【问题描述】:

我有一个返回 long double 的 c 函数。我想使用 ctypes 从 python 调用这个函数,它主要工作。设置so.func.restype = c_longdouble 可以解决问题——除了python 的float 类型是c_double,所以如果返回值大于double,但在long double 的范围内,python 仍然将inf 作为返回值。我在 64 位处理器上,sizeof(long double) 是 16。

在不修改 c 代码的情况下解决这个问题(例如使用十进制类或 numpy)有什么想法吗?

【问题讨论】:

  • 我想做类似的事情并发布了一个问题here,现在意识到我遇到了与您基本相同的问题。 @Autoplectic,我可以知道你最终做了什么吗?

标签: python ctypes


【解决方案1】:

我不确定你是否可以在不修改 C 代码的情况下做到这一点。 ctypes 似乎对long doubles 的支持真的很差——你根本不能像数字一样操纵它们,你所能做的就是在原生float Python 类型之间来回转换它们。

由于 ABI,您甚至不能使用字节数组而不是 c_longdouble 作为返回值 - 浮点值不会像正常返回值一样在 %eax 寄存器或堆栈中返回,它们通过特定于硬件的浮点寄存器传递。

【讨论】:

    【解决方案2】:

    如果您有一个函数返回c_longdouble子类,它将返回 ctypes 包装的字段对象,而不是转换为 python float。然后,您可以从中提取字节(例如,使用 memcpy 到 c_char 数组中)或将对象传递给另一个 C 函数以进行进一步处理。 snprintf函数可以将其格式化为字符串,用于打印或转换成高精度python数值类型。

    import ctypes
    libc = ctypes.cdll['libc.so.6']
    libm = ctypes.cdll['libm.so.6']
    
    class my_longdouble(ctypes.c_longdouble):
        def __str__(self):
            size = 100
            buf = (ctypes.c_char * size)()
            libc.snprintf(buf, size, '%.35Le', self)
            return buf[:].rstrip('\0')
    
    powl = libm.powl
    powl.restype = my_longdouble
    powl.argtypes = [ctypes.c_longdouble, ctypes.c_longdouble]
    
    for i in range(1020,1030):
        res = powl(2,i)
        print '2**'+str(i), '=', str(res)
    

    输出:

    2**1020 = 1.12355820928894744233081574424314046e+307
    2**1021 = 2.24711641857789488466163148848628092e+307
    2**1022 = 4.49423283715578976932326297697256183e+307
    2**1023 = 8.98846567431157953864652595394512367e+307
    2**1024 = 1.79769313486231590772930519078902473e+308
    2**1025 = 3.59538626972463181545861038157804947e+308
    2**1026 = 7.19077253944926363091722076315609893e+308
    2**1027 = 1.43815450788985272618344415263121979e+309
    2**1028 = 2.87630901577970545236688830526243957e+309
    2**1029 = 5.75261803155941090473377661052487915e+309
    

    (请注意,我估计的 35 位精度对于 Intel 处理器上的 long double 计算结果过于乐观,它只有 64 位尾数。您应该使用 %a 而不是 %e/@987654330 @/g 如果您打算转换为不基于十进制表示的格式。)

    【讨论】:

      【解决方案3】:

      如果您需要高精度浮点,请查看 GMPY。

      GMPY 是一个 C 编码的 Python 扩展模块,它包装了 GMP 库,为 Python 代码提供快速多精度算术(整数、有理数和浮点数)、随机数生成、高级数论函数等。

      GMP 包含高级浮点算术函数 (mpf)。如果 C 类型 `double' 没有为应用程序提供足够的精度,这是 GMP 函数类别。此类别中大约有 65 个函数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-05-04
        • 1970-01-01
        • 2018-11-30
        • 1970-01-01
        • 1970-01-01
        • 2014-09-30
        • 2013-09-25
        • 1970-01-01
        相关资源
        最近更新 更多