Pregunta

Tengo una función c que devuelve un long double . Me gustaría llamar a esta función desde Python usando ctypes, y funciona principalmente. la configuración de so.func.restype = c_longdouble hace el truco, excepto que el tipo flotante de python es un c_double por lo tanto, si el valor devuelto es mayor que un doble, pero dentro del En los límites de un doble largo, Python todavía obtiene inf 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, usando la clase o número decimal) sin modificar el código c?

¿Fue útil?

Solución

No estoy seguro de que puedas hacerlo sin modificar el código C. ctypes parece tener un soporte muy malo para las long double s: no puede manipularlos como si fueran números, todo lo que puede hacer es convertirlos entre el código nativo float Tipo de Python.

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

Otros consejos

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 . 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 los cálculos de long double en los procesadores Intel, que solo tienen 64 bits de mantisa. Debe usar % a en lugar de % e / f / g si pretende convertir 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 con código C que envuelve la biblioteca GMP para proporcionar al código Python aritmética de multiprecisión rápida (entero, racional y flotante), generación de números aleatorios, funciones teóricas avanzadas 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 categoría.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top