longs doubles retours et types
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?
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.