Ненужная longdouble арифметика, похоже, не длинная двойная с конверсией

Я играл в C99 quad precision long double. Я понимаю, что (определенная платформа) numpy поддерживает длинный двойной и 128-битные поплавки.

Я столкнулся с чем-то, что не могу объяснить.

Дано:

>>> import numpy as np

Вычислите число, для которого требуется более 64 бит, но менее 128 бит для представления в виде целого числа:

>>> 2**64+2
18446744073709551618          # note the '8' at the end
>>> int(2**64+2)
18446744073709551618          # same obviously

Если я рассчитать тот же номер в C99 128 бит длинный двойной, я получаю 18446744073709551618.000000

Теперь, если я использую numpy long double:

>>> a=np.longdouble(2)
>>> b=np.longdouble(64)
>>> a**b+a
18446744073709551618.0              # all good...

Как насчет этих неправильных результатов:

>>> np.longdouble(2**64+2)
18446744073709551616.0             # Note '6'; appears 2**64 not done in long double
>>> np.longdouble(int(2**64+2))
18446744073709551616.0             # can't force the use of a Python long
>>> n=int(2**64+2)
>>> np.longdouble(n)
18446744073709551616.0
>>> np.longdouble(18446744073709551618)
18446744073709551616.0             # It really does not want to do '8' at the end

Но это работает:

>>> np.longdouble(2**64)+2
18446744073709551618.0

Вопрос: Имеет ли numpy проблемы, которые правильно преобразуют значения в длинные удвоения? Есть ли что-то, что я делаю неправильно?

Ответ 1

Вы пытаетесь выполнить преобразование типов между типами, не являющимися конвертируемыми. Взгляните на стек:

#0  0x00002aaaaab243a0 in PyLong_AsDouble ()
   from libpython2.7.so.1.0
#1  0x00002aaaaab2447a in ?? ()
   from libpython2.7.so.1.0
#2  0x00002aaaaaaf8357 in PyNumber_Float ()
   from libpython2.7.so.1.0
#3  0x00002aaaae71acdc in MyPyFloat_AsDouble (obj=0x2aaaaae93c00)
    at numpy/core/src/multiarray/arraytypes.c.src:40
#4  0x00002aaaae71adfc in LONGDOUBLE_setitem (op=0x2aaaaae93c00, 
    ov=0xc157b0 "", ap=0xbf6ca0)
    at numpy/core/src/multiarray/arraytypes.c.src:278
#5  0x00002aaaae705c82 in PyArray_FromAny (op=0x2aaaaae93c00, 
    newtype=0x2aaaae995960, min_depth=<value optimized out>, max_depth=0, 
    flags=0, context=<value optimized out>)
    at numpy/core/src/multiarray/ctors.c:1664
#6  0x00002aaaae7300ad in longdouble_arrtype_new (type=0x2aaaae9938a0, 
    args=<value optimized out>, __NPY_UNUSED_TAGGEDkwds=<value optimized out>)
    at numpy/core/src/multiarray/scalartypes.c.src:2545

Как вы можете видеть, Python long (целое число с неограниченной точностью) 2**64 + 2 преобразуется в float (т.е. 64-битный double), который теряет точность; float затем используется для инициализации длинного двойника, но точность уже потеряна.

Проблема в том, что 128-битный double не является родным типом Python, поэтому long не имеет собственного преобразования в него, только до 64-битного двойного. Вероятно, NumPy сможет обнаружить эту ситуацию и выполнить собственное преобразование с помощью long API С#), но может быть довольно сложным для относительно небольшой выгоды (вы можете просто сделайте арифметику в np.longdouble с самого начала).

Ответ 2

NumPy не обеспечивает четырехточечную точность на машинах x86. Он обеспечивает доступ к длинному двойному типу C (как это предусмотрено средой компиляции, а с MSVC это может быть 64 бита, а GCC обычно 80 бит) как np.longdouble. Типы np.float96 и np.float128 просто удваиваются до 96 или 128 бит (для выравнивания доступа к памяти). См. документация numpy. Чтобы получить quad-точность в numpy, вам нужно использовать аппаратную платформу и компилятор, где long double является фактической точностью в четырех точках.

В то время как numpy может поддерживать четырехмерную точность с использованием поддержки компилятора (GCC float128) или внешних библиотек, это не было реализовано. Также можно было бы написать сторонний импортируемый модуль, который сделал бы один из них доступным, но это еще не сделано.

Обратите внимание, что даже при использовании np.longdouble легко потерять точность: например, оператор% заставляет numpy передавать свои числа через поплавки python, отбрасывая лишнюю точность.