Dlaczego niektóre liczby tracą dokładność, gdy są przechowywane jako liczby zmiennoprzecinkowe?
Na przykład liczbę dziesiętną 9.2
można wyrazić dokładnie jako stosunek dwóch liczb całkowitych dziesiętnych ( 92/10
), z których oba można wyrazić dokładnie w postaci binarnej ( 0b1011100/0b1010
). Jednak ten sam współczynnik zapisany jako liczba zmiennoprzecinkowa nigdy nie jest dokładnie równy 9.2
:
32-bit "single precision" float: 9.19999980926513671875
64-bit "double precision" float: 9.199999999999999289457264239899814128875732421875
W jaki sposób tak pozornie prosta liczba może być „zbyt duża”, aby wyrazić ją w 64 bitach pamięci?
floating-point
language-agnostic
precision
mhlester
źródło
źródło
Odpowiedzi:
W większości języków programowania liczby zmiennoprzecinkowe są podobne do notacji naukowej : z wykładnikiem wykładniczym i mantysą (zwaną także znaczeniem). Powiedzmy
9.2
, że bardzo prosta liczba to właściwie ułamek:Gdzie jest wykładnik
-49
i mantysa5179139571476070
. Nie można przedstawić w ten sposób niektórych liczb dziesiętnych, ponieważ wykładnik i mantysa muszą być liczbami całkowitymi. Innymi słowy, wszystkie zmiennoprzecinkowe muszą być liczbą całkowitą pomnożoną przez liczbę całkowitą równą 2 .9.2
może być po prostu92/10
, ale 10 nie może być wyrażone jako 2 n, jeżeli n jest ograniczone do wartości całkowitych.Widząc dane
Najpierw kilka funkcji, aby zobaczyć komponenty, które tworzą wersje 32- i 64-bitowe
float
. Przejrzyj je, jeśli zależy ci tylko na wynikach (przykład w Pythonie):Za tą funkcją kryje się duża złożoność i wyjaśnienie byłoby dość styczne, ale jeśli jesteś zainteresowany, ważnym zasobem dla naszych celów jest moduł struct .
Python
float
jest 64-bitową liczbą o podwójnej precyzji. W innych językach, takich jak C, C ++, Java i C #, podwójna precyzja ma osobny typdouble
, który często jest implementowany jako 64 bity.Kiedy wywołujemy tę funkcję w naszym przykładzie
9.2
, oto, co otrzymujemy:Interpretacja danych
Zobaczysz, że podzieliłem wartość zwracaną na trzy składniki. Te elementy to:
Znak
Znak jest przechowywany w pierwszym składniku jako pojedynczy bit. Łatwo to wytłumaczyć:
0
oznacza, że liczba zmiennoprzecinkowa jest liczbą dodatnią;1
oznacza, że jest negatywny. Ponieważ9.2
jest dodatnia, naszą wartością znaku jest0
.Wykładnik potęgowy
Wykładnik jest przechowywany w środkowym składniku jako 11 bitów. W naszym przypadku
0b10000000010
. W systemie dziesiętnym oznacza to wartość1026
. Dziwactwo tego elementu polega na tym, że musisz odjąć liczbę równą 2 (liczba bitów) - 1 - 1, aby uzyskać prawdziwy wykładnik; w naszym przypadku oznacza to odejmowanie0b1111111111
(liczba dziesiętna1023
), aby uzyskać prawdziwy wykładnik0b00000000011
(liczba dziesiętna 3).Mantissa
Mantysa jest przechowywana w trzecim składniku jako 52 bity. Jednak ten element ma również dziwactwo. Aby zrozumieć to dziwactwo, weź pod uwagę liczbę w notacji naukowej, taką jak:
Mantissa byłaby
6.0221413
. Przypomnijmy, że mantysa w notacji naukowej zawsze zaczyna się od pojedynczej niezerowej cyfry. To samo dotyczy binarnych, z tym że binarne mają tylko dwie cyfry:0
i1
. Więc binarna mantysa zawsze zaczyna się od1
! Gdy pływak jest przechowywany,1
przednia część mantysy binarnej jest pomijana, aby zaoszczędzić miejsce; musimy umieścić go z tyłu naszego trzeciego elementu, aby uzyskać prawdziwą mantysę:Wymaga to czegoś więcej niż zwykłego dodania, ponieważ bity przechowywane w naszym trzecim składniku faktycznie reprezentują ułamkową część mantysy, po prawej stronie punktu podstawy .
Kiedy mamy do czynienia z liczbami dziesiętnymi, „przesuwamy przecinek dziesiętny” przez pomnożenie lub podzielenie przez potęgi 10. W trybie binarnym możemy zrobić to samo, mnożąc lub dzieląc przez potęgi 2. Ponieważ nasz trzeci element ma 52 bity, dzielimy przesuń go o 2 52, aby przesunąć o 52 miejsca w prawo:
W notacji dziesiętnej, to tak samo jak dzielenie
675539944105574
przez4503599627370496
dostać0.1499999999999999
. (Jest to jeden z przykładów współczynnika, który można wyrazić dokładnie w postaci binarnej, ale tylko w przybliżeniu w postaci dziesiętnej; więcej szczegółów: 675539944105574/4503599627370496 .)Teraz, gdy przekształciliśmy trzeci składnik w liczbę ułamkową, dodanie
1
daje prawdziwą mantysę.Podsumowanie komponentów
0
pozytywny,1
negatywny1
aby uzyskać prawdziwą mantysęObliczanie liczby
Łącząc wszystkie trzy części, otrzymujemy ten numer binarny:
Które możemy następnie przekonwertować z binarnego na dziesiętny:
I pomnóż, aby odsłonić ostateczną reprezentację liczby, od której zaczęliśmy (
9.2
) po zapisaniu jako wartość zmiennoprzecinkowa:Reprezentowanie jako ułamek
9.2
Teraz, kiedy zbudowaliśmy liczbę, można ją zrekonstruować w prosty ułamek:
Przesuń mantysę na liczbę całkowitą:
Konwertuj na dziesiętne:
Odejmij wykładnik potęgi:
Zamień wykładnik ujemny na podział:
Pomnóż wykładnik:
Co równa się:
9.5
Widać już, że mantysa ma tylko 4 cyfry, po których następuje wiele zer. Ale przejdźmy kroki.
Zbierz binarny zapis naukowy:
Przesuń kropkę dziesiętną:
Odejmij wykładnik potęgi:
Binarny na dziesiętny:
Wykładnik ujemny do podziału:
Pomnóż wykładnik:
Równa się:
Dalsza lektura
źródło
To nie jest pełna odpowiedź ( mhlester omówił już wiele dobrych podstaw, których nie powielę ), ale chciałbym podkreślić, jak bardzo reprezentacja liczby zależy od podstawy, w której pracujesz.
Rozważ ułamek 2/3
W good-ol 'base 10 zwykle zapisujemy to jako coś w rodzaju
Kiedy patrzymy na te reprezentacje, staramy się kojarzyć każdą z nich z ułamkiem 2/3, chociaż tylko pierwsza reprezentacja jest matematycznie równa ułamkowi. Druga i trzecia reprezentacja / przybliżenie ma błąd rzędu 0,001, który jest w rzeczywistości znacznie gorszy niż błąd między 9,2 a 9,1999999999999993. W rzeczywistości druga reprezentacja nie jest nawet poprawnie zaokrąglona! Niemniej jednak nie mamy problemu z 0.666 jako przybliżeniem liczby 2/3, więc nie powinniśmy mieć problemu z przybliżeniem 9.2 w większości programów . (Tak, w niektórych programach ma to znaczenie.)
Bazy liczbowe
Oto, gdzie podstawy liczb są kluczowe. Gdybyśmy próbowali reprezentować 2/3 w bazie 3, wtedy
Innymi słowy, mamy dokładną, skończoną reprezentację dla tej samej liczby poprzez zamianę baz! Odejściem jest to, że nawet jeśli można przekonwertować dowolną liczbę na dowolną bazę, wszystkie liczby wymierne mają dokładne skończone reprezentacje w niektórych bazach, ale w innych nie .
Aby doprowadzić ten punkt do domu, spójrzmy na 1/2. Może cię zaskoczyć, że chociaż ta idealnie prosta liczba ma dokładną reprezentację w bazie 10 i 2, wymaga powtarzalnej reprezentacji w bazie 3.
Dlaczego liczby zmiennoprzecinkowe są niedokładne?
Ponieważ często są one przybliżeniami racjonalnymi, których nie można przedstawić w sposób skończony w podstawie 2 (cyfry powtarzają się), i ogólnie są przybliżeniem liczb rzeczywistych (być może nieracjonalnych), które mogą nie być reprezentowane w skończonej liczbie cyfr w żadnej bazie.
źródło
1/3
takiej samej, jak baza-10 jest idealna dla1/10
. Żadna frakcja nie działa w base-2N
wielokrotnością.π
itp. Zostały usunięte.Podczas gdy wszystkie pozostałe odpowiedzi są dobre, wciąż brakuje jednej rzeczy:
To jest niemożliwe do reprezentowania liczb niewymiernych (np π,
sqrt(2)
,log(3)
, itd.), Dokładnie!I właśnie dlatego nazywane są irracjonalnymi. Żadna ilość bitów na świecie nie wystarczyłaby, aby pomieścić choć jedną z nich. Tylko arytmetyka symboliczna jest w stanie zachować ich precyzję.
Chociaż jeśli ograniczysz swoje potrzeby matematyczne do liczb wymiernych, tylko problem precyzji stanie się możliwy. Będziesz musiał przechowywać parę (prawdopodobnie bardzo dużych) liczb całkowitych
a
ib
przechowywać liczbę reprezentowaną przez ułameka/b
. Cała arytmetyka musiałaby być wykonywana na ułamkach, tak jak w liceum (npa/b * c/d = ac/bd
.).Ale oczywiście będzie nadal działać w tym samym rodzaju kłopoty, kiedy
pi
,sqrt
,log
,sin
, itp są zaangażowane.TL; DR
W przypadku arytmetyki przyspieszanej sprzętowo można przedstawić tylko ograniczoną liczbę liczb wymiernych. Każda niereprezentatywna liczba jest przybliżona. Niektóre liczby (tj. Irracjonalne) nigdy nie mogą być reprezentowane bez względu na system.
źródło
Istnieje nieskończenie wiele liczb rzeczywistych (tak wielu, że nie można ich wyliczyć), i istnieje nieskończenie wiele liczb wymiernych (można je wyliczyć).
Reprezentacja zmiennoprzecinkowa jest skończona (jak wszystko w komputerze), więc nieuchronnie wiele wielu liczb jest niemożliwych do przedstawienia. W szczególności 64 bity pozwalają jedynie rozróżnić tylko 18 446,744,073,709,551,616 różnych wartości (co jest niczym w porównaniu z nieskończonością). Zgodnie ze standardową konwencją 9.2 nie jest jednym z nich. Te, które mogą mieć postać m.2 ^ e dla niektórych liczb całkowitych mi.
Możesz wymyślić inny system numeracji, na przykład 10, gdzie 9.2 miałoby dokładną reprezentację. Ale inne liczby, powiedzmy 1/3, nadal byłyby niemożliwe do przedstawienia.
Należy również pamiętać, że liczby zmiennoprzecinkowe podwójnej precyzji są niezwykle dokładne. Mogą reprezentować dowolną liczbę w bardzo szerokim zakresie z maksymalnie 15 cyframi. Do codziennych obliczeń wystarczą 4 lub 5 cyfr. Nigdy tak naprawdę nie będziesz potrzebował tych 15, chyba że chcesz liczyć każdą milisekundę swojego życia.
źródło
Numery zmiennoprzecinkowe to (nieco upraszczając) system numeracji pozycyjnej z ograniczoną liczbą cyfr i ruchomym punktem bazowym.
Ułamek można wyrazić dokładnie za pomocą skończonej liczby cyfr w systemie numeracji pozycyjnej, jeżeli czynniki pierwsze mianownika (gdy ułamek jest wyrażony w najniższych wartościach) są czynnikami podstawy.
Pierwszymi czynnikami 10 są 5 i 2, więc w podstawie 10 możemy reprezentować dowolną frakcję postaci a / (2 b 5 c ).
Z drugiej strony jedynym czynnikiem podstawowym 2 jest 2, więc w podstawie 2 możemy reprezentować tylko ułamki postaci a / (2 b )
Ponieważ jest to prosty format do pracy i jest wystarczająco dokładny do większości celów. Zasadniczo ten sam powód, dla którego naukowcy używają „notacji naukowej” i zaokrąglają swoje wyniki do rozsądnej liczby cyfr na każdym etapie.
Z pewnością byłoby możliwe zdefiniowanie formatu ułamkowego, z (na przykład) 32-bitowym licznikiem i 32-bitowym mianownikiem. Byłby w stanie reprezentować liczby, których zmiennoprzecinkowy podwójnej precyzji IEEE nie mógłby, ale równie wiele liczb mogłoby być reprezentowanych w zmiennoprzecinkowym podwójnej precyzji, których nie można przedstawić w formacie ułamka o stałym rozmiarze.
Jednak dużym problemem jest to, że taki format jest trudny do wykonania obliczeń. Z dwóch powodów.
Niektóre języki oferują typy ułamkowe, ale zwykle robią to w połączeniu z arbitralną precyzją, dzięki czemu nie trzeba martwić się przybliżeniem ułamków, ale stwarza to własny problem, gdy liczba przechodzi przez dużą liczbę kroków obliczeniowych wielkości mianownika i dlatego miejsce potrzebne na ułamek może eksplodować.
Niektóre języki oferują również dziesiętne typy liczb zmiennoprzecinkowych, są one głównie używane w scenariuszach, w których ważne jest, aby wyniki były zgodne z wcześniejszymi regułami zaokrąglania, które zostały napisane z myślą o ludziach (głównie obliczenia finansowe). Są nieco trudniejsze w obsłudze niż binarne zmiennoprzecinkowe, ale największym problemem jest to, że większość komputerów nie oferuje wsparcia sprzętowego.
źródło
Spróbuj tego
„
decimalValue
” to twoja wartość do przeliczenia.źródło