Czy ma specjalne znaczenie 16331239353195370.0?

88

Używając import numpy as npzauważyłem to

np.tan(np.pi/2)

podaje numer w tytule, a nie np.inf

16331239353195370.0

Ciekawi mnie ten numer. Czy jest to związane z jakimś parametrem precyzji maszyny systemowej? Czy mogłem to obliczyć z czegoś? (Myślę w stylu czegoś podobnego do sys.float_info)

EDYCJA: Ten sam wynik jest rzeczywiście odtwarzalny w innych środowiskach, takich jak Java, octace, matlab ... Jednak sugerowany duplikat nie wyjaśnia dlaczego.

Koleś
źródło
10
Nie podoba mi się ta odpowiedź - jest całkowicie falująca, nie wyjaśniając przyczyny. „Cóż, tan (pi / 2) w radianach jest zasadniczo nieskończone, prawda?” nie wyjaśnia niczego o tym, dlaczego - jak zapytał tutaj PO - odpowiedź nie jest w rzeczywistości np.inf. Ale to nie tylko prosta do wyjaśnienia, dlaczego tak nie jest, ale również wyjaśnić, dlaczego odpowiedź jest dokładnie to, co było postrzegane - i tak zrobiłem ;-)
Tim Peters

Odpowiedzi:

119

pinie jest dokładnie reprezentowalny jako zmiennoprzecinkowy Pythona (taki sam jak doubletyp platformy C ). Stosowane jest najbliższe reprezentatywne przybliżenie.

Oto dokładne przybliżenie używane na moim pudełku (prawdopodobnie takie samo jak na twoim pudełku):

>>> import math
>>> (math.pi / 2).as_integer_ratio()
(884279719003555, 562949953421312)

Aby znaleźć styczną tego współczynnika, przełączę się teraz na wxMaxima:

(%i1) fpprec: 32;
(%o1) 32
(%i2) tan(bfloat(884279719003555) / 562949953421312);
(%o2) 1.6331239353195369755967737041529b16

Więc zasadniczo identyczny z tym, co masz. Stosowane przybliżenie binarne pi/2jest nieco mniejsze niż wartość matematyczna („nieskończona precyzja”) pi/2. Otrzymujesz więc bardzo dużą styczną zamiast infinity. Obliczona tan()jest odpowiednia dla rzeczywistego wejścia!

Z dokładnie tych samych powodów, np.

>>> math.sin(math.pi)
1.2246467991473532e-16

nie zwraca 0. Przybliżenie math.pijest trochę mniejsze niż pi, a wyświetlany wynik jest poprawny, biorąc pod uwagę tę prawdę.

INNE SPOSOBY WIDZENIA math.pi

Istnieje kilka sposobów, aby zobaczyć dokładne przybliżenie w użyciu:

>>> import math
>>> math.pi.as_integer_ratio()
(884279719003555, 281474976710656)

math.pi jest dokładnie równa matematycznej („nieskończonej precyzji”) wartości tego stosunku.

Lub jako dokładny float w notacji szesnastkowej:

>>> math.pi.hex()
'0x1.921fb54442d18p+1'

Lub w sposób najłatwiejszy do zrozumienia dla każdego:

>>> import decimal
>>> decimal.Decimal(math.pi)
Decimal('3.141592653589793115997963468544185161590576171875')

Chociaż może to nie być od razu oczywiste, każda skończona liczba binarna zmiennoprzecinkowa jest dokładnie reprezentowalna jako skończona liczba dziesiętna (odwrotność nie jest prawdą; np. Liczba dziesiętna 0.1nie jest dokładnie reprezentowalna jako skończona liczba binarna zmiennoprzecinkowa), a Decimal(some_float)konstruktor tworzy dokładny odpowiednik.

Oto prawdziwa wartość, pipo której następuje dokładna wartość dziesiętna math.pi, a daszek w trzecim wierszu wskazuje na pierwszą cyfrę, w której się różnią:

true    3.14159265358979323846264338327950288419716939937510...
math.pi 3.141592653589793115997963468544185161590576171875
                         ^

math.pijest teraz taka sama dla „prawie wszystkich” pudełek, ponieważ prawie wszystkie skrzynki używają teraz tego samego binarnego formatu zmiennoprzecinkowego (podwójna precyzja IEEE 754). Możesz użyć dowolnego z powyższych sposobów, aby potwierdzić to na swoim pudełku lub znaleźć dokładne przybliżenie w użyciu, jeśli Twoje pudełko jest wyjątkiem.

Tim Peters
źródło
@Tim Peters - To jest całkowicie jasne. Dla kompletności zgaduję, że ta reprezentacja np.pijest najbliższą racjonalną reprezentacją w obrębie epsilonu systemu?
Aguy
3
Zakładając, że np.pima taką samą wartość jak Python math.pi(nie sprawdzałem, ale możesz ;-)), jest to wartość najbliższa matematycznemu pi reprezentowanemu w natywnym C doubleformacie zmiennoprzecinkowym platformy . Co oznacza podwójną precyzję IEEE 754 na prawie wszystkich pudełkach teraz, a więc najbliższy binarny float z 53 bitami (mantysy) precyzji. Zatem zbiór wymiernych jest ograniczony do postaci, w +/- I * 2**Jktórej liczba całkowita Ijest równa 0 lub 2**52 <= I < 2**53, a zakres liczb całkowitych Jjest wystarczająco szeroki, aby objąć wszystkie wymierne tej postaci w dowolnym miejscu w pobliżu pi.
Tim Peters,
2
A to dlatego, że ja kocham czy „binarne” funkcje trygonometryczne były częściej realizowane. Ponieważ pi nigdy nie może być reprezentowane w postaci racjonalnej, byłoby to przydatne z zestawem funkcji działających na kątach od 0 do 1.
pipe
Cóż, importowali np.pi, nie math.pi.
EKons
2
@ Έρικ Κωνσταντόπουλος math.pi, np.pii scipy.piwszystkie są takie same; są duplikowane tylko dla wygody nazewnictwa; stackoverflow.com/questions/12645547/…
Tim Peters,