2009-01-08 17 views
9

tengo una función c que devuelve long double. Me gustaría llamar a esta función desde python usando ctypes, y funciona principalmente. configuración so.func.restype = c_longdouble hace el truco - excepto que el tipo flotante de Python es un c_double así que si el valor devuelto es más grande que un doble, pero dentro de los límites de un doble largo, python aún obtiene inf como el valor de retorno. estoy en un procesador de 64 bits y sizeof(long double) es 16.long double returns y ctypes

ideas para evitar esto (por ejemplo, usando la clase decimal o numpy) sin modificar el código c?

+0

Quiero hacer algo similar y escribí una pregunta [aquí] (http://stackoverflow.com/questions/25380004/how-do-i-force-usage-of-long-doubles-with-cython), y ahora me di cuenta de que esencialmente tengo el mismo problema que tú. @Autoplectic, ¿puedo saber lo que finalmente hizo? – Abhinav

Respuesta

1

No estoy seguro de poder hacerlo sin modificar el código C. ctypes parece tener un soporte realmente malo para long double s - no puedes manipularlos como números en absoluto, todo lo que puedes hacer es convertirlos de uno a otro entre el tipo de Python nativo float.

Ni siquiera se puede utilizar una matriz de bytes como valor de retorno en lugar de un c_longdouble, debido a la ABI - valores de punto flotante no se devuelven en el registro %eax o en la pila como valores de retorno normales, están repasado a través de los registros de coma flotante específicos del hardware.

0

Si necesita punto flotante de alta precisión, eche un vistazo a GMPY.

GMPY es un módulo de extensión Python codificado en C que envuelve la biblioteca GMP para proporcionar al código Python aritmética rápida de multiples medidas (entero, racional y flotante), generación de números aleatorios, funciones avanzadas de números teóricos y más.

GMP contiene funciones aritméticas de coma flotante de alto nivel (mpf). Esta es la categoría de función GMP que se debe usar si el tipo C "doble" no proporciona la precisión suficiente para una aplicación. Hay alrededor de 65 funciones en esta categoría.

1

Si usted tiene una función devuelve una subclase dec_longdouble, devolverá los ctypes envueltos objeto de campo en lugar de convertir a una pitón float. A continuación, puede extraer los bytes de esto (con memcpy en una matriz c_char, por ejemplo) o pasar el objeto a otra función C para su posterior procesamiento. La función snprintf puede formatearla en una cadena para imprimirla o convertirla en un tipo numérico python de alta precisión.

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) 

Salida:

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 

(Tenga en cuenta que mi estimación de 35 dígitos de precisión resultó ser excesivamente optimista para long double cálculos sobre los procesadores de Intel, que sólo tienen 64 bits de mantisa Debe utilizar. %a en lugar de %e/f/g si tiene la intención de convertir a un formato que no se basa en la representación decimal.)