Domanda
Ho una funzione c che restituisce un long double
. Vorrei chiamare questa funzione da Python usando Ctypes, e funziona principalmente. l'impostazione so.func.restype = c_longdouble
fa il trucco - tranne per il fatto che il tipo float di python è un c_double
quindi se il valore restituito è maggiore di un doppio, ma ben all'interno del limiti di un doppio lungo, python ottiene ancora inf come valore di ritorno. sono su un processore a 64 bit e sizeof (long double)
è 16.
qualche idea su come aggirare questo problema (ad es. usando la classe decimale o numpy) senza modificare il codice c?
Soluzione
Non sono sicuro che puoi farlo senza modificare il codice C. ctypes sembra avere un pessimo supporto per long double
s - non puoi manipolarli come dei numeri, tutto quello che puoi fare è convertirli avanti e indietro tra il float
Tipo Python.
Non puoi nemmeno usare un array di byte come valore di ritorno invece di un c_longdouble
, a causa dell'ABI - i valori in virgola mobile non vengono restituiti nel % eax
register o nello stack come i normali valori di ritorno, vengono passati attraverso i registri a virgola mobile specifici dell'hardware.
Altri suggerimenti
Se hai una funzione restituisci una sottoclasse di c_longdouble
, restituirà l'oggetto campo avvolto in ctypes piuttosto che convertirlo in un float
in pitone . È quindi possibile estrarre i byte da questo (con memcpy
in un array c_char, ad esempio) o passare l'oggetto a un'altra funzione C per un'ulteriore elaborazione. La funzione snprintf
può formattarla in una stringa per la stampa o la conversione in un tipo numerico pitone ad alta precisione.
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 che la mia stima di 35 cifre di precisione si è rivelata eccessivamente ottimistica per i calcoli long double
sui processori Intel, che hanno solo 64 bit di mantissa. Dovresti usare % a
anziché % e
/ f
/ g
se si intende convertire in un formato non basato sulla rappresentazione decimale.)
Se hai bisogno di virgola mobile ad alta precisione, dai un'occhiata a GMPY.
GMPY è un modulo di estensione Python con codice C che avvolge la libreria GMP per fornire al codice Python veloce aritmetica multiprecisione (intero, razionale e float), generazione casuale di numeri, funzioni avanzate di teoria dei numeri e altro ancora.
GMP contiene funzioni aritmetiche in virgola mobile di alto livello ( mpf
) . Questa è la categoria di funzioni GMP da utilizzare se il tipo C `double 'non fornisce una precisione sufficiente per un'applicazione. Ci sono circa 65 funzioni in questa categoria.