Question

J'ai une fonction c qui renvoie un long double . Je voudrais appeler cette fonction à partir de python en utilisant ctypes, et cela fonctionne principalement. définir so.func.restype = c_longdouble fait l'affaire - sauf que le type float de python est un c_double donc si la valeur renvoyée est supérieure à un double, mais bien dans le Aux limites d’un long double, python obtient toujours inf comme valeur de retour. Je suis sur un processeur 64 bits et sizeof (long double) vaut 16.

des idées pour résoudre ce problème (par exemple, en utilisant la classe décimale ou numpy) sans modifier le code c?

Était-ce utile?

La solution

Je ne suis pas sûr que vous puissiez le faire sans modifier le code C. ctypes semble supporter vraiment mal les longs doubles - vous ne pouvez pas les manipuler comme des nombres, tout ce que vous pouvez faire est de les convertir entre le float Type Python.

Vous ne pouvez même pas utiliser un tableau d'octets comme valeur de retour au lieu d'un c_longdouble , car ABI - les valeurs à virgule flottante ne sont pas renvoyées dans le % eax register ou sur la pile, comme pour les valeurs de retour normales, ils sont transmis via les registres à virgule flottante spécifiques au matériel.

Autres conseils

Si vous avez une fonction qui retourne une sous-classe de c_longdouble , elle renverra l’objet champ wrappé ctypes au lieu de la convertir en python float . . Vous pouvez ensuite extraire les octets de cette opération (avec memcpy dans un tableau c_char, par exemple) ou transmettre l'objet à une autre fonction C pour un traitement ultérieur. La fonction snprintf peut le formater en une chaîne à imprimer ou à convertir en un type numérique python de haute précision.

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)

Sortie:

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

(Notez que mon estimation de 35 chiffres de précision s'est avérée excessivement optimiste pour les calculs long double sur les processeurs Intel, qui ne disposent que de 64 bits de mantisse. Vous devez utiliser % a plutôt que % e / f / g si vous souhaitez effectuer la conversion dans un format non basé sur une représentation décimale.)

Si vous avez besoin de virgule flottante de haute précision, consultez GMPY.

GMPY est un module d'extension Python codé en C qui encapsule la bibliothèque GMP pour fournir en arithmétique rapide multi-précision de code Python (entier, rationnel et flottant), génération de nombres aléatoires, fonctions théoriques avancées, et plus encore.

GMP contient des fonctions arithmétiques en virgule flottante de haut niveau ( mpf ). . C'est la catégorie de fonction GMP à utiliser si le type C «double» ne donne pas assez de précision pour une application. Il y a environ 65 fonctions dans cette catégorie.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top