Длинные двойные возвращения и типы
У меня есть функция переменного тока, которая возвращает long double
, Я хотел бы вызвать эту функцию из Python с использованием ctypes, и это в основном работает. установка so.func.restype = c_longdouble
делает трюк - за исключением того, что тип поплавка Python является c_double
поэтому, если возвращаемое значение больше, чем double, но находится в пределах длинного double, python по-прежнему получает inf как возвращаемое значение. я на 64-битном процессоре и sizeof(long double)
16
какие-нибудь идеи обойти это (например, используя десятичный класс или NumPy) без изменения кода C?
3 ответа
Я не уверен, что вы можете сделать это без изменения кода Си. у ctypes действительно плохая поддержка long double
s - вы не можете манипулировать ими как числами, все, что вы можете сделать, это конвертировать их назад и вперед между нативными float
Тип Python.
Вы даже не можете использовать байтовый массив в качестве возвращаемого значения вместо c_longdouble
из-за ABI - значения с плавающей точкой не возвращаются в %eax
регистр или в стеке, как нормальные возвращаемые значения, они передаются через аппаратные регистры с плавающей запятой.
Если у вас есть функция, верните подкласс c_longdouble
, он вернет объект обернутого поля ctypes вместо преобразования в питон float
, Затем вы можете извлечь байты из этого (с memcpy
например, в массив c_char) или передать объект другой функции C для дальнейшей обработки. snprintf
Функция может отформатировать его в строку для печати или преобразования в высокоточный числовой тип Python.
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)
Выход:
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
(Обратите внимание, что моя оценка 35 цифр точности оказалась чрезмерно оптимистичной для long double
расчеты на процессорах Intel, которые имеют только 64 бита мантиссы. Вы должны использовать %a
скорее, чем %e
/f
/g
если вы собираетесь конвертировать в формат, который не основан на десятичном представлении.)
Если вам нужна высокоточная плавающая точка, взгляните на GMPY.
GMPY - это модуль расширения Python на C-коде, который оборачивает библиотеку GMP для предоставления в код Python быстрой многоточной арифметики (целочисленных, рациональных и с плавающей точкой), генерации случайных чисел, расширенных теоретических функций и многого другого.
GMP содержит высокоуровневые арифметические функции с плавающей точкой (mpf
). Эта категория функций GMP используется, если тип C `double'не дает достаточной точности для приложения. В этой категории около 65 функций.