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?

Foi útil?

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 doubles -. 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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top