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?

È stato utile?

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top