2013-08-30 9 views
10

Gram w C99's quad precision długo podwójnie. Rozumiem, że numpy (zależnie od platformy) obsługuje long double i 128 bitów.Numpy longdouble arytmetyczna nie wydaje się być długa podwójna z konwersją

Przeszedłem przez coś, czego nie potrafię wyjaśnić.

Dane:

>>> import numpy as np 

Obliczyć liczbę, która wymaga więcej niż 64 bitów, lecz mniej niż 128 bitów reprezentuje jako liczba całkowita:

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

Jeśli calculate ten sam numer, C99 128 trochę długo podwójnie, dostaję 18446744073709551618.000000

Teraz, jeśli używam numpy długi podwójny:

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

Co o tych nieprawidłowych wyników:

>>> 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 

Ale to działa:

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

pytanie: Czy numpy mają problemy przeliczania wartości poprawnie na długich deblu? Czy coś robię nieprawidłowo?

+0

Czy próbowałeś zrobić to samo w C? Kod, który łączysz * nie * rób tego, co próbujesz zrobić z numpy. Spróbuj najpierw obliczyć '2 ** 64 + 2' jako liczbę całkowitą i * następnie * przypisz ją do' długiego podwójnego '. (Mam na myśli coś takiego: 'long long val = (1 << 64) + 2; long double res = (long double) val') – Bakuriu

+0

@Bakuriu: Tak, próbowałem w C. Tak, próbowałem' n = int (2 ** 64 + 2); np.longdouble (n) 'który nadal nie dodaje wartości 2. – dawg

+1

Używanie wyrażenia' n = (2 ** x + 2); np.longdouble (n) == n' zwróci false dla 'x> 53'. Wygląda na dziwny punkt przełomowy. – Daniel

Odpowiedz

9

Próbujesz wykonać konwersję typu między typami, które nie mogą być przekształcane bezpośrednio. Spójrz na stosie:

#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 

Jak widać, Python long (nieograniczona precyzja całkowita) 2**64 + 2 jest konwertowana do float (czyli 64-bitowy podwójny), które traci precyzji; pływak jest następnie używany do inicjalizacji długich podwójnych, ale precyzja została już utracona.

Problem polega na tym, że 128-bitowe podwójne nie jest rodzimym typem Pythona, więc long nie ma natywnej konwersji, tylko do 64-bitowego podwójnego. Prawdopodobnie NumPy będzie w stanie wykryć tę sytuację i przeprowadzić własną konwersję przy użyciu long C API, ale może być dość skomplikowana ze względnie niewielkiej korzyści (po prostu możesz zacząć od arytmetyki w np.longdouble).

+3

To potencjalnie podstępny błąd, nie? – dawg

+1

@ Po to dlatego [numpy docs] (http://docs.scipy.org/doc/numpy-dev/user/basics.types.html#extended-precision) sugeruje _ "przetestować kod z wartością' 1 + np.finfo (np.longdouble) .eps'. "_ –

2

NumPy nie zapewnia czterokrotnej precyzji na maszynach x86. Zapewnia dostęp do długiego podwójnego typu C (jak zapewnia środowisko kompilacji, z MSVC może to być 64 bity, z GCC jest to zwykle 80 bitów) jako np.longdouble. Typy np.float96 i np.float128 są po prostu długimi sobowtórkami wypełnionymi do 96 lub 128 bitów (dla wyrównanego dostępu do pamięci). Zobacz the numpy docs. Aby uzyskać czterokrotną precyzję w numpy, musisz użyć platformy sprzętowej i kompilatora, gdzie długie podwójne to rzeczywista dokładność czterokrotna.

Choć możliwe jest, że numpy będzie obsługiwać precyzję czterokrotną przy użyciu obsługi kompilatora (GCC float128) lub w bibliotekach zewnętrznych, nie zostało to zaimplementowane. Możliwe byłoby również napisanie importowalnego modułu strony trzeciej, który udostępniłby jeden z nich, ale nie zostało to również zrobione.

Należy również zauważyć, że nawet przy użyciu np.longdouble, łatwo jest stracić precyzję: na przykład, operator% zmusza numpy do przekazania swoich liczb za pomocą python, odrzucając dodatkową precyzję.

Powiązane problemy