Largo doble retorno y ctypes.

Tengo una función de CA que devuelve un long double . Me gustaría llamar a esta función desde Python usando ctypes, y funciona principalmente. configurar so.func.restype = c_longdouble hace el truco, excepto que el tipo flotante de python es un c_double por lo que si el valor devuelto es mayor que un doble, pero dentro de los límites de un doble largo, python todavía se obtiene como valor de retorno . Estoy en un procesador de 64 bits y sizeof(long double) es 16.

¿Alguna idea sobre cómo evitar esto (por ejemplo, usar la clase decimal o numpy) sin modificar el código c?

No estoy seguro de que puedas hacerlo sin modificar el código C. Los ctypes parecen tener un soporte muy malo para el long double s long double . No puedes manipularlos como números, todo lo que puedes hacer es convertirlos entre el tipo de Python float nativo.

Ni siquiera puede utilizar una matriz de bytes como valor de retorno en lugar de c_longdouble , debido a ABI: los valores de punto flotante no se devuelven en el registro %eax o en la stack como los valores de retorno normales, se pasan a través de Los registros de punto flotante específicos del hardware.

Si tiene una función, devuelva una subclase de c_longdouble , devolverá el objeto de campo envuelto ctypes en lugar de convertirlo en un float python. Luego 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 long double en procesadores Intel, que solo tienen 64 bits de mantisa. Debe usar %a lugar de %e / f / g si desea realizar la conversión a un formato que no se basa en la representación decimal.)

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

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

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