longos retornos duplos e ctypes
Pergunta
Eu tenho uma função c que retorna um long double
. eu gostaria de chamar essa função de python usando ctypes, e principalmente trabalha. definindo so.func.restype = c_longdouble
faz o truque - exceto tipo float que de python é um c_double
por isso, se o valor retornado é maior do que uma dupla, mas bem dentro dos limites de um longo duplas, python ainda recebe inf como o valor de retorno. eu estou em um processador de 64 bits e sizeof(long double)
é 16.
quaisquer ideias sobre como se locomover isso (por exemplo, usando a classe decimal ou numpy) sem modificar o código c?
Solução
Eu não tenho certeza que você pode fazê-lo sem modificar o código C. ctypes parece ter realmente ruim suporte para long double
s -. você não pode manipulá-los como números em tudo, tudo o que você pode fazer é convertê-los frente e para trás entre o float
tipo Python nativa
Você não pode mesmo usar um array de bytes como valor de retorno em vez de um c_longdouble
, por causa da ABI - valores de ponto flutuante não são devolvidos no registo %eax
ou na pilha como valores de retorno normais, eles são passou por registradores de ponto flutuante específico do hardware.
Outras dicas
Se você tem uma função de retorno de um subclasse de c_longdouble
, ele irá retornar os ctypes objeto de campo embrulhadas em vez de converter a um float
python. Pode então extrair os bytes deste (com memcpy
em uma matriz c_char, por exemplo) ou passar o objecto para uma outra função C para posterior processamento. A função snprintf
pode formatá-lo em uma string para impressão ou conversão em um tipo numérico de alta precisão 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)
Output:
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
(Nota que a minha estimativa de 35 dígitos de precisão acabou por ser excessivamente otimista para cálculos long double
em processadores Intel, que apenas têm 64 bits de mantissa. Você deve usar %a
em vez de %e
/ f
/ g
se você pretende converter para um formato que não é baseado na representação decimal.)
Se você precisar de alta precisão de ponto flutuante, ter um olhar para GMPY.
GMPY é um módulo de extensão do Python com código de C que envolve a biblioteca GMP para fornecer ao código Python rápido multiprecision aritmética (inteiro, racional e float), geração de números aleatórios, funções avançadas número teórico, e muito mais.
GMP contém de alto nível de ponto flutuante funções aritméticas (mpf
). Esta é a categoria função GMP para usar se o tipo C `double' não dá precisão suficiente para um aplicativo. Há cerca de 65 funções nesta categoria.