long double return和ctypes

时间:2009-01-08 06:00:48

标签: python ctypes

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

有关解决此问题的任何想法(例如使用十进制类或numpy)而不修改c代码?

3 个答案:

答案 0 :(得分:1)

我不确定如果不修改C代码就可以做到。 ctypes似乎对long double的支持非常糟糕 - 你根本无法像数字一样操纵它们,你只能在原生的float Python类型之间来回转换它们。

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

答案 1 :(得分:1)

如果你有一个函数返回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

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

答案 2 :(得分:0)

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

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

GMP包含高级浮点算术函数(mpf)。如果C类型“double”没有为应用程序提供足够的精度,那么这是要使用的GMP函数类别。此类别中约有65种功能。