Вопрос

у меня есть функция c, которая возвращает long double.я бы хотел вызвать эту функцию из python, используя ctypes, и это в основном работает.настройка so.func.restype = c_longdouble делает трюк - за исключением того, что тип float в python является c_double таким образом, если возвращаемое значение больше, чем double, но находится в пределах long double , python все равно получает inf в качестве возвращаемого значения.я использую 64-битный процессор и sizeof(long double) составляет 16.

есть какие-нибудь идеи, как обойти это (напримериспользуя десятичный класс или numpy) без изменения кода c?

Это было полезно?

Решение

Я не уверен, что вы можете сделать это без изменения кода Си. Кажется, что ctypes действительно плохо поддерживают long double s - вы не можете манипулировать ими как числами, все, что вы можете сделать, это конвертировать их назад и вперед между собственным float Тип Python.

Вы даже не можете использовать байтовый массив в качестве возвращаемого значения вместо c_longdouble из-за ABI - значения с плавающей запятой не возвращаются в % eax register или в стеке, как обычные возвращаемые значения, они передаются через аппаратно-зависимые регистры с плавающей запятой.

Другие советы

Если у вас есть функция, верните подкласс из c_longdouble, он вернет объект поля , завернутый в ctypes , вместо преобразования в python 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 функций.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top