Arytmetyka zmiennoprzecinkowa — Floating-point arithmetic

Wczesny elektromechaniczny programowalny komputer Z3 zawierał arytmetykę zmiennoprzecinkową (replika na wystawie w Deutsches Museum w Monachium ).

W obliczeniowej , zmiennoprzecinkową arytmetyka ( FP ) jest arytmetyczny używając stereotypowej reprezentacji liczb rzeczywistych jako przybliżenie do wspierania kompromis pomiędzy zakresem i precyzją. Z tego powodu obliczenia zmiennoprzecinkowe są często używane w systemach z bardzo małymi i bardzo dużymi liczbami rzeczywistymi, które wymagają szybkiego czasu przetwarzania. Ogólnie rzecz biorąc, liczba zmiennoprzecinkowa jest reprezentowana w przybliżeniu za pomocą stałej liczby cyfr znaczących ( significand ) i skalowana za pomocą wykładnika w pewnej stałej podstawie; podstawa skalowania to zwykle dwa, dziesięć lub szesnaście. Liczba, którą można dokładnie przedstawić, ma następującą postać:

gdzie significand jest liczbą całkowitą , podstawa jest liczbą całkowitą większą lub równą dwa, a wykładnik jest również liczbą całkowitą. Na przykład:

Termin zmiennoprzecinkowy odnosi się do faktu, że podstawa liczby ( przecinek dziesiętny lub, częściej w komputerach, przecinek binarny ) może „pływać”; oznacza to, że można go umieścić w dowolnym miejscu w stosunku do cyfr znaczących liczby. Ta pozycja jest wskazywana jako składnik wykładniczy, a zatem reprezentację zmiennoprzecinkową można traktować jako rodzaj notacji naukowej .

System zmiennoprzecinkowy może być użyty do przedstawienia, za pomocą stałej liczby cyfr, liczb o różnych rzędach wielkości : np. odległość między galaktykami lub średnica jądra atomowego może być wyrażona tą samą jednostką długości. Wynikiem tego dynamicznego zakresu jest to, że liczby, które mogą być reprezentowane, nie są równomiernie rozmieszczone; różnica między dwiema kolejnymi reprezentowalnymi liczbami zmienia się w zależności od wybranej skali.

Liczby zmiennoprzecinkowe o pojedynczej precyzji na osi liczbowej : zielone linie oznaczają wartości, które można przedstawić.
Rozszerzona wersja powyżej pokazująca oba znaki reprezentowalnych wartości

Przez lata w komputerach używano różnych reprezentacji zmiennoprzecinkowych. W 1985 roku ustanowiono standard IEEE 754 dla arytmetyki zmiennoprzecinkowej, a od lat 90. najczęściej spotykanymi reprezentacjami są te zdefiniowane przez IEEE.

Szybkość operacji zmiennoprzecinkowych, powszechnie mierzona w postaci FLOPS , jest ważną cechą systemu komputerowego , zwłaszcza w zastosowaniach wymagających intensywnych obliczeń matematycznych.

Jednostka zmiennoprzecinkowa (FPU, potocznie koprocesor matematyczny ) jest częścią systemu komputerowego specjalnie zaprojektowanego do wykonywania operacji na liczbach zmiennoprzecinkowych.

Przegląd

Liczb zmiennoprzecinkowych

Reprezentacja liczba określa jakiś sposób kodowania numeru, zwykle jako ciąg cyfr.

Istnieje kilka mechanizmów, dzięki którym ciągi cyfr mogą reprezentować liczby. W powszechnym zapisie matematycznym ciąg cyfr może mieć dowolną długość, a położenie punktu podstawy jest wskazywane przez umieszczenie tam wyraźnego znaku „punktu” (kropki lub przecinka). Jeśli punkt podstawy nie zostanie określony, ciąg niejawnie reprezentuje liczbę całkowitą, a nieopisany punkt podstawy będzie znajdował się poza prawym końcem ciągu, obok najmniej znaczącej cyfry. W systemach stałoprzecinkowych pozycja w ciągu jest określona dla punktu podstawy. Tak więc schemat stałoprzecinkowy może polegać na użyciu ciągu 8 cyfr dziesiętnych z kropką dziesiętną w środku, przy czym „00012345” będzie reprezentować 0001.2345.

W notacji naukowej dana liczba jest skalowana przez potęgę 10 , tak aby mieściła się w pewnym zakresie — zwykle od 1 do 10, przy czym punkt podstawy pojawia się bezpośrednio po pierwszej cyfrze. Współczynnik skalowania, jako potęga dziesiątki, jest następnie wskazywany oddzielnie na końcu liczby. Na przykład, okres orbitalny Jupiter jest księżyca Io jest152.853.5047 sekund, wartość, która byłaby reprezentowana w standardowej notacji naukowej jako1,528535047 × 10 5 sekund.

Reprezentacja zmiennoprzecinkowa jest podobna w koncepcji do notacji naukowej. Logicznie rzecz biorąc, liczba zmiennoprzecinkowa składa się z:

  • Podpisany (czyli dodatnią lub ujemną) cyfrowy ciąg danej długości w danej bazy (lub radix ). Ten ciąg cyfr jest określany jako znaczenie , mantysa lub współczynnik . Długość znaku znaczącego określa precyzję, z jaką mogą być reprezentowane liczby. Zakłada się, że pozycja punktu podstawy zawsze znajduje się gdzieś w obrębie znaczącej — często tuż za lub tuż przed najbardziej znaczącą cyfrą lub na prawo od skrajnej prawej (najmniej znaczącej) cyfry. Ten artykuł jest ogólnie zgodny z konwencją, że punkt podstawy jest ustawiany tuż po najbardziej znaczącej (najbardziej lewej) cyfrze.
  • Wykładnik liczby całkowitej ze znakiem (nazywany również charakterystyką lub skalą ), który modyfikuje wielkość liczby.

Aby uzyskać wartość liczby zmiennoprzecinkowej, znaczna jest mnożona przez podstawę podniesioną do potęgi wykładnika , co odpowiada przesunięciu punktu podstawy z jego domniemanej pozycji o liczbę miejsc równą wartości wykładnika — do w prawo, jeśli wykładnik jest dodatni, lub w lewo, jeśli wykładnik jest ujemny.

Na przykładzie podstawy 10 (znana notacja dziesiętna ) liczba152,853.5047 , który ma dziesięć cyfr dziesiętnych precyzji, jest reprezentowany jako significand1 528 535 047 wraz z 5 jako wykładnikiem. Aby określić wartość rzeczywistą, po pierwszej cyfrze znaczącego umieszcza się kropkę dziesiętną, a wynik mnoży się przez 10 5, aby otrzymać1,528535047 × 10 5 , lub152.853.5047 . Przy przechowywaniu takiego numeru podstawa (10) nie musi być przechowywana, ponieważ będzie taka sama dla całego zakresu obsługiwanych numerów, a zatem można ją wywnioskować.

Symbolicznie ta końcowa wartość to:

gdzie s jest znaczącym (z pominięciem dowolnego domniemanego przecinka dziesiętnego), p jest precyzją (liczba cyfr w znaczącej), b jest podstawą (w naszym przykładzie jest to liczba dziesięć ), a e jest wykładnikiem.

Historycznie do reprezentowania liczb zmiennoprzecinkowych używano kilku podstaw liczbowych, przy czym podstawa dwa ( dwójkowa ) była najbardziej powszechna, a następnie podstawa dziesięć ( dziesiętna liczba zmiennoprzecinkowa ) i inne mniej popularne odmiany, takie jak podstawa szesnastka ( zmiennoprzecinkowa szesnastkowa). ), podstawa osiem (zmiennoprzecinkowa ósemkowa), podstawa czwarta (czwartorzędowa liczba zmiennoprzecinkowa), podstawa trzecia ( zrównoważona trójka zmiennoprzecinkowa ), a nawet podstawa 256 i podstawa65 536 .

Liczba zmiennoprzecinkowa jest liczbą wymierną , ponieważ może być reprezentowana jako jedna liczba całkowita podzielona przez drugą; na przykład1,45 × 10 3 to (145/100) × 1000 lub145 000/100 . Podstawa określa ułamki, które mogą być reprezentowane; na przykład 1/5 nie może być reprezentowana dokładnie jako liczba zmiennoprzecinkowa przy użyciu podstawy dwójkowej, ale 1/5 może być przedstawiona dokładnie przy użyciu podstawy dziesiętnej (0,2 , lub2 x 10 -1 ). Jednak 1/3 nie może być dokładnie reprezentowana przez binarną (0.010101...) lub dziesiętną (0.333...), ale przy podstawie 3 jest trywialna (0,1 lub 1×3 −1 ) . Okazje, w których występują nieskończone ekspansje, zależą od podstawy i jej czynników pierwszych .

Sposób, w jaki znaczące (w tym jego znak) i wykładnik są przechowywane w komputerze, zależy od implementacji. Popularne formaty IEEE są szczegółowo opisane w dalszej części i w innych miejscach, ale jako przykład w binarnej reprezentacji zmiennoprzecinkowej o pojedynczej precyzji (32-bity) , a więc significand jest ciągiem 24 bitów . Na przykład pierwsze 33 bity liczby π to:

W tym rozwinięciu binarnym oznaczmy pozycje od 0 (bit najbardziej od lewej lub najbardziej znaczący bit) do 32 (bit najbardziej na prawo). 24-bitowy znak zatrzyma się na pozycji 23, pokazanej jako podkreślony bit0 powyżej. Następny bit, na pozycji 24, nazywany jest bitem zaokrąglania lub bitem zaokrąglania . Służy do zaokrąglania przybliżenia 33-bitowego do najbliższej liczby 24-bitowej (istnieją określone zasady dla wartości połówkowych , co nie ma miejsca w tym przypadku). Ten kawałek, który jest1 w tym przykładzie jest dodawane do liczby całkowitej utworzonej przez 24 bity po lewej stronie, otrzymując:

Kiedy jest on przechowywany w pamięci przy użyciu kodowania IEEE 754, staje się znaczącym s . Zakłada się, że znaczące i ma punkt binarny po prawej stronie skrajnego lewego bitu. Zatem binarna reprezentacja π jest obliczana od lewej do prawej w następujący sposób:

gdzie p jest precyzją (24 w tym przykładzie), n jest pozycją bitu znaczącego od lewej (począwszy od0 i zakończenie o23 tutaj) a e jest wykładnikiem (1 w tym przykładzie).

Może być wymagane, aby najbardziej znacząca cyfra znaczącego liczby niezerowej była niezerowa (z wyjątkiem sytuacji, gdy odpowiedni wykładnik byłby mniejszy niż minimalny). Ten proces nazywa się normalizacją . Dla formatów binarnych (używających tylko cyfr0 i1 ), ta niezerowa cyfra jest koniecznie1 . Dlatego nie musi być reprezentowany w pamięci; pozwalając formatowi na jeszcze jedną precyzję. Zasada ta jest różnie nazywany wiodącą konwencję bitowy , z niejawnego konwencję bitowy , z ukrytą konwencję bitowy , lub zakładany konwencję bitowej .

Alternatywy dla liczb zmiennoprzecinkowych

Reprezentacja zmiennoprzecinkowa jest zdecydowanie najczęstszym sposobem przedstawiania w komputerach przybliżenia do liczb rzeczywistych. Istnieją jednak alternatywy:

  • Reprezentacja stałoprzecinkowa wykorzystuje operacje sprzętowe na liczbach całkowitych sterowane przez implementację programową określonej konwencji dotyczącej lokalizacji przecinka dwójkowego lub dziesiętnego, na przykład 6 bitów lub cyfr od prawej strony. Sprzęt do manipulowania tymi reprezentacjami jest mniej kosztowny niż zmiennoprzecinkowy i może być również używany do wykonywania normalnych operacji na liczbach całkowitych. Binarny punkt stały jest zwykle używany w aplikacjach specjalnego przeznaczenia na procesorach wbudowanych, które mogą wykonywać tylko arytmetykę liczb całkowitych, ale dziesiętny punkt stały jest powszechny w zastosowaniach komercyjnych.
  • Logarytmiczne systemy liczbowe (LNS) reprezentują liczbę rzeczywistą przez logarytm jej wartości bezwzględnej i bit znaku. Rozkład wartości jest podobny do zmiennoprzecinkowego, ale krzywa wartości do reprezentacji ( tj . wykres funkcji logarytmicznej) jest gładka (z wyjątkiem 0). W przeciwieństwie do arytmetyki zmiennoprzecinkowej, w logarytmicznym systemie liczb mnożenie, dzielenie i potęgowanie są proste do zaimplementowania, ale dodawanie i odejmowanie są złożone. Arytmetyka ( symetryczna ) indeksu poziomu (LI i SLI) Charlesa Clenshawa, Franka Olvera i Petera Turnera jest schematem opartym na uogólnionej reprezentacji logarytmicznej .
  • Reprezentacja zmiennoprzecinkowa stożkowa , która nie wydaje się być używana w praktyce.
  • Tam, gdzie pożądana jest większa precyzja, arytmetyka zmiennoprzecinkowa może być zaimplementowana (zwykle w oprogramowaniu) ze znaczącymi o zmiennej długości (a czasami wykładnikami), których wielkość zależy od rzeczywistych potrzeb i w zależności od przebiegu obliczeń. Nazywa się to arytmetyką zmiennoprzecinkową o dowolnej precyzji .
  • Rozszerzenia zmiennoprzecinkowe to kolejny sposób na uzyskanie większej precyzji, korzystający ze sprzętu zmiennoprzecinkowego: liczba jest reprezentowana jako nieoceniona suma kilku liczb zmiennoprzecinkowych. Przykładem jest arytmetyka podwójna podwójna , czasami używana dla typu C long double.
  • Niektóre proste liczby wymierne ( np. 1/3 i 1/10) nie mogą być reprezentowane dokładnie w binarnych liczbach zmiennoprzecinkowych, bez względu na precyzję. Użycie innej podstawy pozwala na przedstawienie niektórych z nich ( np. 1/10 dziesiętnie zmiennoprzecinkowo), ale możliwości pozostają ograniczone. Pakiety oprogramowania wykonujące arytmetykę wymierną reprezentują liczby jako ułamki z licznikiem całkowitym i mianownikiem, dzięki czemu mogą dokładnie reprezentować dowolną liczbę wymierną. Takie pakiety zazwyczaj wymagają arytmetyki " bignum " dla poszczególnych liczb całkowitych .
  • Arytmetyka interwałowa pozwala na reprezentowanie liczb jako interwałów i uzyskanie gwarantowanych granic wyników. Generalnie opiera się na innej arytmetyce, w szczególności na liczbach zmiennoprzecinkowych.
  • Systemy algebry komputerowej, takie jak Mathematica , Maxima i Maple , często radzą sobie z liczbami niewymiernymi jak lub w całkowicie "formalny" sposób, bez zajmowania się konkretnym kodowaniem znaczącego. Taki program może dokładnie oceniać wyrażenia takie jak " ", ponieważ jest zaprogramowany do bezpośredniego przetwarzania podstawowej matematyki, zamiast używania przybliżonych wartości dla każdego obliczenia pośredniego.

Historia

W 1914 Leonardo Torres y Quevedo zaprojektował elektro-mechaniczny wersję Charles Babbage „s Analytical silnika i objęły arytmetyki zmiennoprzecinkowej. W 1938 Konrad Zuse z Berlina ukończył Z1 , pierwszy binarny, programowalny komputer mechaniczny ; wykorzystuje 24-bitową binarną reprezentację liczby zmiennoprzecinkowej z 7-bitowym wykładnikiem ze znakiem, 17-bitową sygnaturą (w tym jednym bitem niejawnym) i bitem znaku. Bardziej niezawodny przekaźnik Z3 oparty na przekaźniku , ukończony w 1941 roku, ma reprezentacje zarówno dodatnich, jak i ujemnych nieskończoności; w szczególności implementuje zdefiniowane operacje z nieskończonością, takie jak , i zatrzymuje się na niezdefiniowanych operacjach, takich jak .

Konrad Zuse , architekt komputera Z3 , który używa 22-bitowej binarnej reprezentacji zmiennoprzecinkowej

Zuse zaproponował również, ale nie ukończył, starannie zaokrągloną arytmetykę zmiennoprzecinkową, która obejmuje reprezentacje NaN, wyprzedzając cechy standardu IEEE o cztery dekady. W przeciwieństwie do tego, von Neumann odradzał stosowanie liczb zmiennoprzecinkowych dla maszyny IAS z 1951 r. , argumentując, że arytmetyka stałoprzecinkowa jest lepsza.

Pierwszym komercyjnym komputerem ze sprzętem zmiennoprzecinkowym był komputer Zuse Z4 zaprojektowany w latach 1942-1945. W 1946 roku Bell Laboratories wprowadziło Mark V, który zaimplementował dziesiętne liczby zmiennoprzecinkowe .

Pilot ACE ma binarny arytmetyki zmiennoprzecinkowej, i rozpoczęła działalność w 1950 roku w National Physical Laboratory w Wielkiej Brytanii . Trzydzieści trzy zostały później sprzedane jako English Electric DEUCE . Arytmetyka jest faktycznie zaimplementowana w oprogramowaniu, ale przy częstotliwości zegara jednego megaherca szybkość operacji zmiennoprzecinkowych i stałoprzecinkowych w tej maszynie była początkowo większa niż w wielu konkurencyjnych komputerach.

Masowo produkowany IBM 704 nastąpił w 1954 roku; wprowadzono użycie tendencyjnego wykładnika . Przez wiele dziesięcioleci sprzęt zmiennoprzecinkowy był zwykle opcją opcjonalną, a komputery, które go posiadały, były uważane za "komputery naukowe" lub " obliczenia naukowe " (SC) (zobacz także rozszerzenia dla obliczeń naukowych (XSC )). Dopiero po wprowadzeniu na rynek Intel i486 w 1989 roku komputery osobiste ogólnego przeznaczenia były standardowo wyposażone w sprzętową funkcję zmiennoprzecinkową.

Seria UNIVAC 1100/2200 , wprowadzona w 1962 roku, obsługiwała dwie reprezentacje zmiennoprzecinkowe:

  • Pojedyncza precyzja : 36 bitów, zorganizowanych jako 1-bitowy znak, 8-bitowy wykładnik i 27-bitowy znak.
  • Podwójna precyzja : 72 bity, zorganizowane jako 1-bitowy znak, 11-bitowy wykładnik i 60-bitowy znak.

IBM 7094 , wprowadzono również w 1962 roku, obsługuje pojedynczej precyzji i podwójnej precyzji reprezentacji, ale bez związku z przedstawicielstw UNIVAC użytkownika. Rzeczywiście, w 1964 roku IBM wprowadził szesnastkowe reprezentacje zmiennoprzecinkowe w swoich komputerach mainframe System/360 ; te same reprezentacje są nadal dostępne do użytku w nowoczesnych systemach z/Architecture . Jednak w 1998 roku IBM dołączył do swoich komputerów mainframe binarną arytmetykę zmiennoprzecinkową zgodną z IEEE; w 2005 roku IBM dodał również zgodną z IEEE arytmetykę zmiennoprzecinkową dziesiętną.

Początkowo komputery używały wielu różnych reprezentacji liczb zmiennoprzecinkowych. Brak standaryzacji na poziomie mainframe był stałym problemem na początku lat 70. dla tych, którzy piszą i utrzymują kod źródłowy wyższego poziomu; te normy producentów zmiennoprzecinkowych różniły się rozmiarami słów, reprezentacjami, zachowaniem zaokrąglania i ogólną dokładnością operacji. Zgodność zmiennoprzecinkowa między wieloma systemami obliczeniowymi wymagała pilnej standaryzacji na początku lat 80., co doprowadziło do stworzenia standardu IEEE 754, gdy słowo 32-bitowe (lub 64-bitowe) stało się powszechne. Standard ten był w dużej mierze oparty na propozycji firmy Intel, która projektowała koprocesor numeryczny i8087 ; Motorola, która projektowała 68000 mniej więcej w tym samym czasie, również wniosła znaczący wkład.

W 1989 roku matematyk i informatyk William Kahan został uhonorowany Nagrodą Turinga za bycie głównym architektem stojącym za tą propozycją; pomagał mu jego uczeń (Jerome Coonen) i profesor wizytujący (Harold Stone).

Wśród innowacji x86 są:

  • Precyzyjnie określona reprezentacja zmiennoprzecinkowa na poziomie ciągu bitowego, dzięki czemu wszystkie zgodne komputery interpretują wzorce bitowe w ten sam sposób. Umożliwia to dokładne i wydajne przenoszenie liczb zmiennoprzecinkowych z jednego komputera na drugi (po uwzględnieniu endianness ).
  • Precyzyjnie określone zachowanie dla operacji arytmetycznych: Wynik musi być wygenerowany tak, jakby użyto nieskończenie dokładnej arytmetyki do uzyskania wartości, która jest następnie zaokrąglana zgodnie z określonymi regułami. Oznacza to, że zgodny program komputerowy zawsze dawał ten sam wynik, gdy otrzymał określone dane wejściowe, łagodząc w ten sposób niemal mistyczną reputację, jaką wypracowały obliczenia zmiennoprzecinkowe ze względu na swoje dotychczas pozornie niedeterministyczne zachowanie.
  • Zdolność wyjątkowych warunków (przepełnienie, dzielenie przez zero itp.) do propagowania przez obliczenia w łagodny sposób, a następnie obsługiwana przez oprogramowanie w sposób kontrolowany.

Zakres liczb zmiennoprzecinkowych

Liczba zmiennoprzecinkowa składa się z dwóch składników stałoprzecinkowych , których zakres zależy wyłącznie od liczby bitów lub cyfr w ich reprezentacji. Podczas gdy składowe liniowo zależą od ich zakresu, zakres zmiennoprzecinkowy liniowo zależy od zakresu istotności i wykładniczo od zakresu składowej wykładniczej, która wiąże z liczbą wybitnie szerszy zakres.

W typowym systemie komputerowym binarna liczba zmiennoprzecinkowa podwójnej precyzji (64-bitowa) ma współczynnik 53 bitów (w tym 1 bit implikowany), wykładnik 11 bitów i 1 bit znaku. Ponieważ 2 10 = 1024, pełny zakres dodatnich normalnych liczb zmiennoprzecinkowych w tym formacie wynosi od 2 -1022  ≈ 2 × 10 -308 do około 2 1024  ≈ 2 × 10 308 .

Liczba znormalizowanych liczb zmiennoprzecinkowych w systemie ( B , P , L , U ) gdzie

  • B to podstawa systemu,
  • P jest precyzją znaczącego (w bazie B ),
  • L jest najmniejszym wykładnikiem systemu,
  • U jest największym wykładnikiem systemu,

jest .

Istnieje najmniejsza dodatnia znormalizowana liczba zmiennoprzecinkowa,

Poziom niedopełnienia = UFL = ,

który ma 1 jako cyfrę wiodącą i 0 dla pozostałych cyfr znaczącego i najmniejszą możliwą wartość dla wykładnika.

Istnieje największa liczba zmiennoprzecinkowa,

Poziom przepełnienia = OFL = ,

który ma B − 1 jako wartość dla każdej cyfry znaczącego i największą możliwą wartość dla wykładnika.

Ponadto istnieją wartości reprezentowalne ściśle pomiędzy −UFL i UFL. Mianowicie zera dodatnie i ujemne , a także liczby zdenormalizowane .

IEEE 754: zmiennoprzecinkowy we współczesnych komputerach

IEEE znormalizowane reprezentacji komputerowego dla binarnych liczb zmiennoprzecinkowych w standardzie IEEE 754 (aka IEC 60559) w roku 1985. Ten pierwszy standard następuje niemal wszystkich nowoczesnych maszyn. Został zrewidowany w 2008 roku . Komputery mainframe IBM obsługują własny format szesnastkowy zmiennoprzecinkowy i dziesiętny zmiennoprzecinkowy IEEE 754-2008 oprócz formatu binarnego IEEE 754. Seria Cray T90 miała wersję IEEE, ale SV1 nadal używa formatu zmiennoprzecinkowego Cray.

Norma przewiduje wiele ściśle powiązanych formatów, różniących się jedynie kilkoma szczegółami. Pięć z tych formatów nazywa się formatami podstawowymi , a inne nazywane są formatami o rozszerzonej precyzji i formatem o rozszerzonej precyzji . Trzy formaty są szczególnie szeroko stosowane w sprzęcie komputerowym i językach:

  • Pojedyncza precyzja (binary32), zwykle używana do reprezentowania typu „float” w rodzinie języków C (chociaż nie jest to gwarantowane ). Jest to format binarny, który zajmuje 32 bity (4 bajty), a jego znaczenie ma precyzję 24 bitów (około 7 cyfr dziesiętnych).
  • Podwójna precyzja (binary64), zwykle używana do reprezentowania typu „double” w rodzinie języków C (chociaż nie jest to gwarantowane ). Jest to format binarny, który zajmuje 64 bity (8 bajtów), a jego znaczenie ma precyzję 53 bitów (około 16 cyfr dziesiętnych).
  • Format podwójnie rozszerzony , również niejednoznacznie nazywany „rozszerzoną precyzją”. Jest to format binarny, który zajmuje co najmniej 79 bitów (80, jeśli nie jest stosowana reguła bitów ukrytych/niejawnych) i ma precyzję co najmniej 64 bitów (około 19 cyfr dziesiętnych). Standardy C99 i C11 z rodziny języków C, w swoim załączniku F („IEC 60559 arytmetyka zmiennoprzecinkowa”), zalecają, aby taki rozszerzony format był dostarczany jako „ długi podwójny ”. Format spełniający minimalne wymagania (64-bitowe znaczenie i precyzja, 15-bitowy wykładnik, a więc pasujący do 80 bitów) zapewnia architektura x86 . Często na takich procesorach ten format może być używany z „długim podwójnym”, chociaż rozszerzona precyzja nie jest dostępna w MSVC. W celu wyrównania wiele narzędzi przechowuje tę 80-bitową wartość w przestrzeni 96-bitowej lub 128-bitowej. Na innych procesorach „długa podwójna” może oznaczać większy format, taki jak poczwórna precyzja lub po prostu podwójna precyzja, jeśli jakakolwiek forma rozszerzonej precyzji nie jest dostępna.

Zwiększenie precyzji reprezentacji zmiennoprzecinkowej ogólnie zmniejsza ilość skumulowanego błędu zaokrąglenia spowodowanego przez obliczenia pośrednie. Mniej popularne formaty IEEE obejmują:

  • Poczwórna precyzja (binarny128). Jest to format binarny, który zajmuje 128 bitów (16 bajtów), a jego znaczenie ma precyzję 113 bitów (około 34 cyfry dziesiętne).
  • Formaty zmiennoprzecinkowe dziesiętne64 i dziesiętne128 zmiennoprzecinkowe. Te formaty, wraz z formatem decimal32 , są przeznaczone do prawidłowego zaokrąglania dziesiętnego.
  • Połowa precyzji , zwana także binary16, 16-bitową wartością zmiennoprzecinkową. Jest używany w języku graficznym NVIDIA Cg oraz w standardzie openEXR.

Każda liczba całkowita o wartości bezwzględnej mniejszej niż 224 może być dokładnie reprezentowana w formacie pojedynczej precyzji, a każda liczba całkowita o wartości bezwzględnej mniejszej niż 2,53 może być dokładnie reprezentowana w formacie podwójnej precyzji. Co więcej, można przedstawić szeroki zakres potęg o wartości 2 razy większej od takiej liczby. Te właściwości są czasami używane w przypadku danych czysto całkowitych, aby uzyskać 53-bitowe liczby całkowite na platformach, które mają zmiennoprzecinkowe o podwójnej precyzji, ale tylko 32-bitowe liczby całkowite.

Norma określa pewne specjalne wartości i ich reprezentację: dodatnia nieskończoność (+∞), ujemna nieskończoność (−∞), ujemne zero (−0) różne od zwykłego („dodatniego”) zera i wartości „nie liczba” ( NaN ).

Porównanie liczb zmiennoprzecinkowych, zgodnie z definicją w standardzie IEEE, różni się nieco od zwykłego porównywania liczb całkowitych. Ujemne i dodatnie zero porównują równe, a każdy NaN porównuje nierówne z każdą wartością, w tym z samą sobą. Wszystkie skończone liczby zmiennoprzecinkowe są ściśle mniejsze niż +∞ i ściśle większe niż −∞ i są uporządkowane w taki sam sposób jak ich wartości (w zbiorze liczb rzeczywistych).

Reprezentacja wewnętrzna

Liczby zmiennoprzecinkowe są zwykle pakowane do danych komputerowych jako bit znaku, pole wykładnika i znaczek lub mantysa, od lewej do prawej. W przypadku formatów binarnych IEEE 754 (podstawowych i rozszerzonych), które mają istniejące implementacje sprzętowe, są one podzielone w następujący sposób:

Rodzaj Znak Wykładnik potęgowy Pole znaczące Całkowita liczba bitów Błąd wykładniczy Dokładność bitów Liczba cyfr dziesiętnych
Połowa ( IEEE 754-2008 ) 1 5 10 16 15 11 ~3,3
Pojedynczy 1 8 23 32 127 24 ~7,2
Podwójnie 1 11 52 64 1023 53 ~15,9
x86 rozszerzona precyzja 1 15 64 80 16383 64 ~19,2
Kwadrat 1 15 112 128 16383 113 ~34,0

Chociaż wykładnik może być dodatni lub ujemny, w formatach binarnych jest przechowywany jako liczba bez znaku, do której dodano stałe „odchylenie”. Wartości wszystkich zer w tym polu są zarezerwowane dla zer i liczb podnormalnych ; wartości wszystkich 1 są zarezerwowane dla nieskończoności i NaN. Zakres wykładniczy dla liczb znormalizowanych to [-126, 127] dla pojedynczej precyzji, [-1022, 1023] dla podwójnej lub [-16382, 16383] dla quad. Liczby znormalizowane wykluczają wartości podnormalne, zera, nieskończoności i NaN.

W formatach wymiany binarnej IEEE wiodący 1 bit znormalizowanej znaczącej nie jest w rzeczywistości przechowywany w danych komputerowych. Nazywa się to bitem „ukrytym” lub „ukrytym”. Z tego powodu format pojedynczej precyzji ma w rzeczywistości znaczący z 24 bitami precyzji, format podwójnej precyzji ma 53, a quad ma 113.

Na przykład powyżej pokazano, że π, zaokrąglone do 24 bitów precyzji, ma:

  • znak = 0 ; e = 1 ; s = 11001001000111111011011 (w tym bit ukryty)

Suma wykładnika wykładnika (127) i wykładnika (1) wynosi 128, więc jest to reprezentowane w formacie pojedynczej precyzji jako

  • 0 10000000 1001001000111111011011 (bez bitu ukrytego) = 40490FDB jako liczba szesnastkowa .

Przykładem układu dla 32-bitowej liczby zmiennoprzecinkowej jest

Float przykład.svg

i układ 64-bitowy jest podobny .

Wartości specjalne

Podpisany zero

W standardzie IEEE 754 zero jest podpisane, co oznacza, że ​​istnieje zarówno „zero dodatnie” (+0), jak i „zero ujemne” (−0). W większości środowisk0 wykonawczych zero dodatnie jest zwykle drukowane jako „ ”, a zero ujemne jako „ -0”. Obie wartości zachowują się tak samo w porównaniach liczbowych, ale niektóre operacje zwracają różne wyniki dla +0 i -0. Na przykład 1/(−0) zwraca ujemną nieskończoność, podczas gdy 1/+0 zwraca dodatnią nieskończoność (tak, że zachowana jest tożsamość 1/(1/±∞) = ±∞). Inne wspólne funkcje z nieciągłością przy x =0, które mogą traktować +0 i -0 inaczej, obejmują log ( x ), signum ( x ) i pierwiastek kwadratowy z y + xi dla dowolnej liczby ujemnej y . Podobnie jak w przypadku każdego schematu aproksymacji, operacje obejmujące „ujemne zero” mogą czasami powodować zamieszanie. Na przykład, w IEEE 754, x = y nie zawsze implikuje 1/ x = 1/ y , ponieważ 0 = -0 ale 1/0 ≠ 1/-0 .

Liczby podnormalne

Wartości podnormalne wypełniają lukę niedomiaru wartościami, w których bezwzględna odległość między nimi jest taka sama, jak dla sąsiednich wartości tuż poza odstępem niedomiaru. Jest to ulepszenie w stosunku do starszej praktyki polegającej na tym, że luka niedomiaru jest po prostu zerowa, a wyniki niedomiaru zostały zastąpione przez zero (przepływ do zera).

Współczesny sprzęt zmiennoprzecinkowy zwykle obsługuje wartości podnormalne (jak również wartości normalne) i nie wymaga emulacji programowej dla wartości podnormalnych.

Nieskończoności

Nieskończoności rozszerzonej linii liczb rzeczywistych mogą być reprezentowane w zmiennoprzecinkowych typach danych IEEE, tak jak zwykłe wartości zmiennoprzecinkowe, takie jak 1, 1,5 itd. Nie są to w żaden sposób wartości błędów, chociaż często (ale nie zawsze, ponieważ zależy to od zaokrąglenia) używane jako wartości zastępcze w przypadku przepełnienia. W przypadku wyjątku dzielenia przez zero zwracana jest dodatnia lub ujemna nieskończoność jako dokładny wynik. Nieskończoność można również wprowadzić jako liczbę (jak makro „INFINITY” w języku C lub „∞”, jeśli język programowania zezwala na taką składnię).

IEEE 754 wymaga obsługi nieskończoności w rozsądny sposób, na przykład

  • (+∞) + (+7) = (+∞)
  • (+∞) × (−2) = (−∞)
  • (+∞) × 0 = NaN – nie ma sensu robić

NaNs

IEEE 754 określa specjalną wartość zwaną „Not a Number” (NaN), która ma być zwrócona w wyniku pewnych „nieprawidłowych” operacji, takich jak 0/0, ∞×0 lub sqrt(−1). Ogólnie rzecz biorąc, NaNs będą propagowane, tj. większość operacji z udziałem NaN da NaN, chociaż funkcje, które dałyby określony wynik dla dowolnej wartości zmiennoprzecinkowej, zrobią to również dla NaNs, np. NaN ^ 0 = 1. Istnieją dwa rodzaje sieci NaN: domyślne ciche sieci NaN i opcjonalnie sieci sygnalizacyjne . Sygnalizacja NaN w dowolnej operacji arytmetycznej (w tym porównaniach liczbowych) spowoduje zasygnalizowanie wyjątku „nieprawidłowa operacja” .

Reprezentacja NaN określona przez standard ma kilka nieokreślonych bitów, które można wykorzystać do zakodowania typu lub źródła błędu; ale nie ma standardu dla tego kodowania. Teoretycznie sygnalizacja NaN może być używana przez system wykonawczy do oznaczania niezainicjowanych zmiennych lub rozszerzania liczb zmiennoprzecinkowych o inne specjalne wartości bez spowalniania obliczeń za pomocą zwykłych wartości, chociaż takie rozszerzenia nie są powszechne.

Uzasadnienie projektu IEEE 754

Williama Kahana . Główny architekt koprocesora zmiennoprzecinkowego Intel 80x87 i standardu zmiennoprzecinkowego IEEE 754 .

Powszechnie uważa się, że bardziej ezoteryczne cechy standardu IEEE 754 omawiane tutaj, takie jak rozszerzone formaty, NaN, nieskończoności, subnormalne itp., są interesujące tylko dla analityków numerycznych lub dla zaawansowanych aplikacji numerycznych; w rzeczywistości jest odwrotnie: te funkcje mają na celu zapewnienie bezpiecznych, solidnych ustawień domyślnych dla programistów nieskomplikowanych numerycznie, oprócz obsługi zaawansowanych bibliotek numerycznych przez ekspertów. Główny projektant IEEE 754, William Kahan zauważa, że ​​niepoprawne jest „... [uznanie] cechy IEEE Standard 754 dla binarnej arytmetyki zmiennoprzecinkowej, które ... [są] nie doceniane jako funkcje, których można używać tylko w liczbach Fakty są zupełnie odwrotne. W 1977 roku te cechy zostały zaprojektowane w Intel 8087, aby służyć jak najszerszemu rynkowi... Analiza błędów mówi nam, jak zaprojektować arytmetykę zmiennoprzecinkową, taką jak IEEE Standard 754, umiarkowanie tolerancyjną -co oznacza ignorancję wśród programistów".

  • Specjalne wartości, takie jak nieskończoność i NaN, zapewniają, że arytmetyka zmiennoprzecinkowa jest algebraicznie kompletna: każda operacja zmiennoprzecinkowa daje dobrze zdefiniowany wynik i domyślnie nie spowoduje przerwania maszyny ani pułapki. Co więcej, wybory wartości specjalnych zwracanych w wyjątkowych przypadkach zostały zaprojektowane tak, aby w wielu przypadkach dać poprawną odpowiedź. Na przykład, w arytmetyce IEEE 754, ułamki ciągłe, takie jak R(z):= 7 - 3/[z - 2 - 1/(z - 7 + 10/[z - 2 - 2/(z - 3)]) ] da poprawną odpowiedź na wszystkich danych wejściowych, ponieważ dzielenie potencjału przez zero, np. dla z = 3 , jest prawidłowo obsługiwane przez podanie +nieskończoności, a więc takie wyjątki można bezpiecznie zignorować. Jak zauważył Kahan, nieobsłużona pułapka, która nastąpiła po przepełnieniu konwersji liczby zmiennoprzecinkowej na 16-bitowe liczby całkowite, która spowodowała utratę rakiety Ariane 5 , nie miałaby miejsca przy domyślnych zasadach zmiennoprzecinkowych IEEE 754.
  • Liczby podnormalne zapewniają, że dla skończonych liczb zmiennoprzecinkowych x i y, x − y = 0 wtedy i tylko wtedy, gdy x = y, jak oczekiwano, ale które nie były zgodne z wcześniejszymi reprezentacjami zmiennoprzecinkowymi.
  • O uzasadnieniu projektowym 80-bitowego formatu x87 Kahan zauważa: „Ten rozszerzony format jest przeznaczony do użytku, z nieznaczną utratą prędkości, dla wszystkich, z wyjątkiem najprostszej arytmetyki z liczbami zmiennoprzecinkowymi i podwójnymi. Na przykład należy go używać dla zmiennych typu scratch w pętlach, które implementują powtarzanie, takie jak ocena wielomianowa, iloczyny skalarne, ułamki częściowe i ciągłe. Często zapobiega to przedwczesnemu przekroczeniu/niedomiarowi lub poważnemu lokalnemu anulowaniu, które może zepsuć proste algorytmy. Obliczanie wyników pośrednich w rozszerzonym formacie z dużą precyzją i rozszerzonym wykładnik ma precedensów w historycznej praktyki naukowej obliczeń oraz w projektowaniu kalkulatorów naukowych np Hewlett-Packard „s kalkulatory finansowe wykonywane funkcje arytmetyczne i finansowych do trzech ważniejszych miejsc po przecinku niż przechowywane lub wyświetlane. Implementacja rozszerzonej precyzji umożliwiła łatwe opracowywanie standardowych bibliotek funkcji elementarnych, które normalnie dawały wyniki o podwójnej precyzji w obrębie jednej jednostki na ostatnim miejscu (ULP) z dużą szybkością.
  • Prawidłowe zaokrąglanie wartości do najbliższej reprezentowalnej wartości pozwala uniknąć systematycznych błędów w obliczeniach i spowalnia wzrost błędów. Zaokrąglanie powiązań do nawet usuwa błąd statystyczny, który może wystąpić przy dodawaniu podobnych liczb.
  • Zaokrąglanie ukierunkowane miało służyć jako pomoc w sprawdzaniu granic błędów, np. w arytmetyce przedziałowej . Wykorzystywany jest również przy realizacji niektórych funkcji.
  • Matematyczna podstawa operacji, w szczególności poprawne zaokrąglanie, pozwala na udowodnienie własności matematycznych i zaprojektowanie algorytmów zmiennoprzecinkowych, takich jak algorytm sumowania 2Sum, Fast2Sum i Kahan , np. w celu zwiększenia dokładności lub stosunkowo łatwej realizacji podprogramów arytmetycznych o wielokrotnej precyzji.

Właściwość formatów o pojedynczej i podwójnej precyzji polega na tym, że ich kodowanie umożliwia łatwe ich sortowanie bez użycia sprzętu zmiennoprzecinkowego. Ich bity zinterpretowane jako liczba całkowita uzupełniająca do dwóch już poprawnie sortują pozytywy, a negatywy są odwrócone. Z xor, aby odwrócić bit znaku dla wartości dodatnich i wszystkie bity dla wartości ujemnych, wszystkie wartości stają się sortowalne jako liczby całkowite bez znaku (z -0 < +0 ). Nie jest jasne, czy ta właściwość jest przeznaczona.

Inne godne uwagi formaty zmiennoprzecinkowe

Oprócz szeroko stosowanych formatów standardowych IEEE 754 , inne formaty zmiennoprzecinkowe są używane lub były używane w pewnych obszarach specyficznych dla domeny.

  • Formacie Microsoft Binary (MBF) został opracowany dla produktów językowych Microsoft podstawowego, w tym pierwszym produktem w historii firmy Microsoft Altair BASIC (1975), TRS-80 POZIOM II , CP / M 's MBASIC , IBM PC 5150 ' s BASICA , MS DOS „s GW-BASIC i QuickBASIC przed wersją 4.00. QuickBASIC w wersji 4.00 i 4.50 został przełączony na format IEEE 754-1985, ale może powrócić do formatu MBF za pomocą opcji polecenia /MBF. MBF został zaprojektowany i opracowany na symulowanym Intel 8080 przez Monte Davidoffa , współlokatora Billa Gatesa , wiosną 1975 roku dla MITS Altair 8800 . Pierwsze wydanie z lipca 1975 r. obsługiwało format pojedynczej precyzji (32 bity) ze względu na koszt 4-kilobajtowej pamięci MITS Altair 8800 . W grudniu 1975 roku do 8-kilobajtowej wersji dodano format podwójnej precyzji (64 bity). Format wariantu pojedynczej precyzji (40 bitów) został przyjęty dla innych procesorów, w szczególności MOS 6502 ( Apple // , Commodore PET , Atari ), Motorola 6800 (MITS Altair 680) i Motorola 6809 ( TRS-80 Color Computer ). Wszystkie produkty językowe firmy Microsoft od 1975 do 1987 używały formatu binarnego firmy Microsoft, dopóki firma Microsoft nie przyjęła standardowego formatu IEEE-754 we wszystkich swoich produktach, począwszy od 1988 r. do ich bieżących wydań. MBF składa się z formatu MBF pojedynczej precyzji (32 bity, „6-cyfrowy PODSTAWOWY”), MBF rozszerzonej precyzji (40 bitów, „9-cyfrowy PODSTAWOWY”) oraz formatu MBF podwójnej precyzji (64 bity) ; każdy z nich jest reprezentowany przez 8-bitowy wykładnik, po którym następuje bit znaku, po którym następuje significand odpowiednio 23, 31 i 55 bitów.
  • Format Bfloat16 wymaga takiej samej ilości pamięci (16 bitów), jak IEEE 754 pół precyzji formatu , ale przydziela 8 bitów wykładnika zamiast 5, co zapewnia taki sam zakres jak IEEE 754 pojedynczej precyzji liczbę. Kompromisem jest zmniejszona precyzja, ponieważ końcowe pole znaczące jest zmniejszone z 10 do 7 bitów. Format ten wykorzystywany jest głównie w szkoleniu modeli uczenia maszynowego , gdzie zakres jest bardziej wartościowy niż precyzja. Wiele akceleratorów uczenia maszynowego zapewnia obsługę sprzętową tego formatu.
  • Format TensorFloat-32 zapewnia najlepsze z formatów Bfloat16 i półprecyzyjne, z 8 bitami wykładnika jako pierwszym i 10 bitami końcowego pola znaczącego jako drugim. Ten format został wprowadzony przez firmę Nvidia , która zapewnia obsługę sprzętową w rdzeniach Tensor swoich procesorów graficznych opartych na architekturze Nvidia Ampere. Wadą tego formatu jest jego całkowity rozmiar 19 bitów, który nie jest potęgą 2. Jednak według Nvidii format ten powinien być używany wyłącznie wewnętrznie przez sprzęt w celu przyspieszenia obliczeń, podczas gdy wejścia i wyjścia powinny być przechowywane w pamięci 32-bitowy format IEEE 754 o pojedynczej precyzji.
Specyfikacje formatów Bfloat16 i TensorFloat-32 w porównaniu ze standardowymi formatami IEEE 754 o połowie i pojedynczej precyzji
Rodzaj Znak Wykładnik potęgowy Końcowe pole znaczące Całkowita liczba bitów
Połowa precyzji 1 5 10 16
Bfloat16 1 8 7 16
TensorFloat-32 1 8 10 19
Pojedyncza precyzja 1 8 23 32

Liczby reprezentatywne, przeliczanie i zaokrąglanie

Ze swej natury wszystkie liczby wyrażone w formacie zmiennoprzecinkowym są liczbami wymiernymi z kończącym rozwinięciem o odpowiedniej podstawie (na przykład kończącym rozwinięciem dziesiętnym o podstawie 10 lub kończącym rozwinięciem binarnym o podstawie 2). Liczby niewymierne , takie jak π lub √2 lub niewykańczające liczby wymierne, muszą być aproksymowane. Liczba cyfr (lub bitów) precyzji ogranicza również zbiór liczb wymiernych, które można dokładnie przedstawić. Na przykład, liczba dziesiętna 123456789 nie może być dokładnie reprezentowana jeśli tylko osiem cyfr dziesiętnych z dokładnością są dostępne (byłoby to w zaokrągleniu do jednego z dwóch międzystrefowych wartości reprezentowalna, 12345678 x 10 1 lub 12345679 x 10 1 ), to samo odnosi się do innych niż cyfry -terminating (. 5 być zaokrąglone albo .55555555 lub .55555556).

Jeśli liczba jest reprezentowana w jakimś formacie (takim jak ciąg znaków), który nie jest natywną reprezentacją zmiennoprzecinkową obsługiwaną w implementacji komputerowej, będzie wymagać konwersji, zanim będzie mogła zostać użyta w tej implementacji. Jeśli liczba może być reprezentowana dokładnie w formacie zmiennoprzecinkowym, konwersja jest dokładna. Jeśli nie ma dokładnej reprezentacji, konwersja wymaga wyboru liczby zmiennoprzecinkowej, która ma reprezentować oryginalną wartość. Wybrana reprezentacja będzie miała inną wartość niż oryginalna, a tak dostosowana wartość nazywana jest wartością zaokrągloną .

To, czy liczba wymierna ma rozwinięcie kończące, zależy od podstawy. Na przykład w bazie 10 liczba 1/2 ma rozszerzenie końcowe (0,5), podczas gdy liczba 1/3 nie (0,333...). W bazie 2 kończą się tylko wymierne liczby z mianownikami będącymi potęgami 2 (np. 1/2 lub 3/16). Każdy wymierny z mianownikiem, który ma czynnik pierwszy inny niż 2, będzie miał nieskończoną ekspansję binarną. Oznacza to, że liczby, które wydają się być krótkie i dokładne, gdy są zapisywane w formacie dziesiętnym, mogą wymagać przybliżenia podczas konwersji na binarne liczby zmiennoprzecinkowe. Na przykład liczba dziesiętna 0.1 nie może być reprezentowana w binarnych liczbach zmiennoprzecinkowych o jakiejkolwiek skończonej precyzji; dokładna reprezentacja binarna miałaby ciąg „1100” ciągnący się w nieskończoność:

e = -4; s = 1100110011001100110011001100110011...,

gdzie, jak poprzednio, s jest znaczącym, a e jest wykładnikiem.

Po zaokrągleniu do 24 bitów staje się to

e = -4; s = 110011001100110011001101,

co w rzeczywistości jest 0.100000001490116119384765625 w postaci dziesiętnej.

Jako dalszy przykład, liczba rzeczywista π , reprezentowana binarnie jako nieskończony ciąg bitów, to

11.00100100001111110110010101000100010000101101000110000100011010011...

ale jest

11.0011000000111111011011

po przybliżeniu przez zaokrąglenie z dokładnością do 24 bitów.

W binarnym zmiennoprzecinkowym pojedynczej precyzji jest to reprezentowane jako s  = 1.1001001000111111011011 z e  = 1. Ma to wartość dziesiętną

3.141592 7410125732421875,

natomiast dokładniejsze przybliżenie prawdziwej wartości π to

3.14159265358979323846264338327950 ...

Wynik zaokrąglania różni się od prawdziwej wartości o około 0,03 części na milion i odpowiada dziesiętnej reprezentacji π w pierwszych 7 cyfrach. Różnica polega na błędzie dyskretyzacji i jest ograniczona przez epsilon maszyny .

Różnica arytmetyczna między dwiema kolejnymi reprezentowalnymi liczbami zmiennoprzecinkowymi, które mają ten sam wykładnik, nazywana jest jednostką na ostatnim miejscu (ULP). Na przykład, jeśli nie ma reprezentowalnej liczby leżącej pomiędzy reprezentowalnymi liczbami 1.45a70c22 hex i 1.45a70c24 hex , ULP wynosi 2×16 -8 lub 2 -31 . W przypadku liczb z wykładnikiem o podstawie 2 równym 0, tj. liczb o wartości bezwzględnej większej lub równej 1, ale mniejszej niż 2, ULP wynosi dokładnie 2 -23 lub około 10-7 w pojedynczej precyzji i dokładnie 2 -53 lub około 10-16 z podwójną precyzją. Obowiązkowe zachowanie sprzętu zgodnego z IEEE polega na tym, że wynik mieści się w połowie wartości ULP.

Tryby zaokrąglania

Zaokrąglanie jest używane, gdy dokładny wynik operacji zmiennoprzecinkowej (lub konwersji na format zmiennoprzecinkowy) wymagałby większej liczby cyfr niż liczba cyfr w znaczącej. IEEE 754 wymaga poprawnego zaokrąglania : to znaczy, że zaokrąglony wynik jest taki, jakby do obliczenia wartości użyto nieskończenie dokładnej arytmetyki, a następnie zaokrąglono (chociaż w implementacji potrzebne są tylko trzy dodatkowe bity, aby to zapewnić). Istnieje kilka różnych schematów zaokrąglania (lub trybów zaokrąglania ). Historycznie typowym podejściem było skrócenie . Od czasu wprowadzenia standardu IEEE 754 domyślna metoda ( zaokrąglanie do najbliższej, powiązanie z parzystymi , czasami nazywana zaokrągleniem bankiera) jest częściej używana. Ta metoda zaokrągla idealny (nieskończenie dokładny) wynik operacji arytmetycznej do najbliższej możliwej do przedstawienia wartości i podaje tę reprezentację jako wynik. W przypadku remisu wybierana jest wartość, która sprawiłaby, że znaczek kończyłby się na parzystą cyfrę. Standard IEEE 754 wymaga zastosowania takiego samego zaokrąglania do wszystkich podstawowych operacji algebraicznych, w tym pierwiastków kwadratowych i konwersji, gdy istnieje wynik liczbowy (nie-NaN). Oznacza to, że wyniki operacji IEEE 754 są całkowicie określone we wszystkich bitach wyniku, z wyjątkiem reprezentacji NaNs. (Funkcje „biblioteczne”, takie jak cosinus i log, nie są wymagane).

Dostępne są również alternatywne opcje zaokrąglania. IEEE 754 określa następujące tryby zaokrąglania:

  • zaokrąglaj do najbliższej, gdzie wiąże się z zaokrągleniem do najbliższej parzystej cyfry na wymaganej pozycji (domyślny i zdecydowanie najpopularniejszy tryb)
  • zaokrąglaj do najbliższej, gdzie remisy zaokrąglają się od zera (opcjonalne dla binarnych liczb zmiennoprzecinkowych i powszechnie używane w systemie dziesiętnym)
  • zaokrąglaj w górę (w kierunku +∞; ujemne wyniki zatem zaokrąglaj w kierunku zera)
  • zaokrąglaj w dół (w kierunku −∞; ujemne wyniki zatem zaokrąglaj od zera)
  • zaokrąglić do zera (obcięcie; jest to podobne do zwykłego zachowania konwersji liczb zmiennoprzecinkowych na liczby całkowite, które konwertują -3,9 na -3 i 3.9 na 3)

Tryby alternatywne są przydatne, gdy ilość wprowadzanego błędu musi być ograniczona. Aplikacje, które wymagają ograniczonego błędu, to zmiennoprzecinkowa o wielu precyzji i arytmetyka interwałowa . Alternatywne tryby zaokrąglania są również przydatne w diagnozowaniu niestabilności numerycznej: jeśli wyniki podprogramu różnią się znacznie między zaokrągleniem do + i − nieskończoności, to prawdopodobnie jest on numerycznie niestabilny i obarczony błędem zaokrąglenia.

Konwersja binarna na dziesiętna z minimalną liczbą cyfr

Konwersja binarnej liczby zmiennoprzecinkowej o podwójnej precyzji na ciąg dziesiętny jest powszechną operacją, ale algorytm dający wyniki, które są zarówno dokładne, jak i minimalne, pojawił się w druku dopiero w 1990 r., wraz z Dragon4 firmy Steele i White. Niektóre z ulepszeń od tego czasu obejmują:

  • dtoa.c Davida M. Gaya , praktyczna implementacja open-source wielu pomysłów w Dragon4.
  • Grisu3, z przyspieszeniem 4x, ponieważ eliminuje użycie bignum . Musi być używany z rezerwą, ponieważ nie działa w ~0,5% przypadków.
  • Errol3, zawsze skuteczny algorytm podobny do Grisu3, ale wolniejszy niż. Najwyraźniej nie tak dobry, jak szybko kończący Grisu z rezerwą.
  • Ryū, zawsze odnoszący sukcesy algorytm, który jest szybszy i prostszy niż Grisu3.

Wiele nowoczesnych środowisk wykonawczych języka używa Grisu3 z rezerwą Dragon4.

Konwersja dziesiętna na binarną

Problem parsowania ciągu dziesiętnego na binarną reprezentację FP jest złożony, a dokładny parser pojawił się dopiero w pracy Clingera z 1990 roku (zaimplementowanej w dtoa.c). Dalsze prace również poszły w kierunku szybszego analizowania.

Operacje arytmetyczne zmiennoprzecinkowe

Dla ułatwienia prezentacji i zrozumienia, w przykładach zostanie użyta podstawa dziesiętna z dokładnością do 7 cyfr, tak jak w formacie IEEE 754 decimal32 . Podstawowe zasady są takie same w każdym radix lub precyzji, oprócz tego, że normalizacja jest opcjonalne (nie ma wpływu na wartość liczbową wyniku). Tutaj s oznacza znaczenie, a e oznacza wykładnik.

Dodawanie i odejmowanie

Prostą metodą dodawania liczb zmiennoprzecinkowych jest przedstawienie ich najpierw tym samym wykładnikiem. W poniższym przykładzie druga liczba jest przesunięta w prawo o trzy cyfry, a następnie jedna jest kontynuowana zgodnie ze zwykłą metodą dodawania:

  123456.7 = 1.234567 × 10^5
  101.7654 = 1.017654 × 10^2 = 0.001017654 × 10^5
  Hence:
  123456.7 + 101.7654 = (1.234567 × 10^5) + (1.017654 × 10^2)
                      = (1.234567 × 10^5) + (0.001017654 × 10^5)
                      = (1.234567 + 0.001017654) × 10^5
                      =  1.235584654 × 10^5

Szczegółowo:

  e=5;  s=1.234567     (123456.7)
+ e=2;  s=1.017654     (101.7654)
  e=5;  s=1.234567
+ e=5;  s=0.001017654  (after shifting)
--------------------
  e=5;  s=1.235584654  (true sum: 123558.4654)

To jest prawdziwy wynik, dokładna suma argumentów. Zostanie on zaokrąglony do siedmiu cyfr, a następnie w razie potrzeby znormalizowany. Ostateczny wynik to

  e=5;  s=1.235585    (final sum: 123558.5)

Najniższe trzy cyfry drugiego argumentu (654) są zasadniczo stracone. To jest błąd zaokrąglenia . W skrajnych przypadkach suma dwóch liczb niezerowych może być równa jednej z nich:

  e=5;  s=1.234567
+ e=−3; s=9.876543
  e=5;  s=1.234567
+ e=5;  s=0.00000009876543 (after shifting)
----------------------
  e=5;  s=1.23456709876543 (true sum)
  e=5;  s=1.234567         (after rounding and normalization)

W powyższych przykładach koncepcyjnych wydaje się, że duża liczba dodatkowych cyfr musiałaby być zapewniona przez sumator, aby zapewnić prawidłowe zaokrąglanie; jednakże, w przypadku dodawania lub odejmowania binarnego przy użyciu ostrożnych technik implementacji, poza precyzją operandów należy przenieść tylko bit zabezpieczający, bit zaokrąglający i jeden dodatkowy bit lepki .

Inny problem utraty istotności pojawia się, gdy odejmuje się przybliżenia do dwóch prawie równych liczb. W poniższym przykładzie e  = 5; s  = 1,234571 i e  = 5; s  = 1,234567 są przybliżeniami do wymiernych 123457,1467 i 123456.659.

  e=5;  s=1.234571
− e=5;  s=1.234567
----------------
  e=5;  s=0.000004
  e=−1; s=4.000000 (after rounding and normalization)

Różnica zmiennoprzecinkowa jest obliczana dokładnie dlatego, że liczby są zbliżone — lemat Sterbenza gwarantuje to, nawet w przypadku niedomiaru, gdy obsługiwany jest stopniowy niedomiar . Mimo to różnica między oryginalnymi liczbami wynosi e  = -1; s  = 4,877000, co różni się o ponad 20% od różnicy e  = -1; s  = 4.000000 przybliżeń. W skrajnych przypadkach wszystkie znaczące cyfry precyzji mogą zostać utracone. To anulowanie ilustruje niebezpieczeństwo przy założeniu, że wszystkie cyfry obliczonego wyniku są znaczące. Radzenie sobie z konsekwencjami tych błędów jest tematem analizy numerycznej ; zobacz także Problemy z dokładnością .

Mnożenie i dzielenie

Aby pomnożyć, znaczące są mnożone, podczas gdy wykładniki są dodawane, a wynik jest zaokrąglany i normalizowany.

  e=3;  s=4.734612
× e=5;  s=5.417242
-----------------------
  e=8;  s=25.648538980104 (true product)
  e=8;  s=25.64854        (after rounding)
  e=9;  s=2.564854        (after normalization)

Podobnie dzielenia dokonuje się poprzez odjęcie wykładnika dzielnika od wykładnika dywidendy i podzielenie significand dywidendy przez significand dzielnika.

Nie ma problemów z anulowaniem lub absorpcją z mnożeniem lub dzieleniem, chociaż małe błędy mogą się kumulować w miarę wykonywania kolejnych operacji. W praktyce sposób te operacje są wykonywane w logice cyfrowej może być dość skomplikowane (patrz Bootha algorytm mnożenia i algorytm Division ). Aby uzyskać szybką i prostą metodę, zobacz metodę Hornera .

Radzenie sobie z wyjątkowymi przypadkami

Obliczenia zmiennoprzecinkowe w komputerze mogą napotkać trzy rodzaje problemów:

  • Operacja może być matematycznie niezdefiniowana, na przykład ∞/∞ lub dzielenie przez zero .
  • Operacja może być w zasadzie legalna, ale nie jest obsługiwana przez określony format, na przykład obliczanie pierwiastka kwadratowego z -1 lub odwrotnego sinusa z 2 (w obu przypadkach otrzymujemy liczby zespolone ).
  • Operacja może co do zasady być legalna, ale wynik może być niemożliwy do przedstawienia w określonym formacie, ponieważ wykładnik jest zbyt duży lub zbyt mały, aby można go było zakodować w polu wykładnika. Takie zdarzenie nazywamy przepełnieniem (zbyt duży wykładnik), niedomiarem (zbyt mały wykładnik) lub denormalizacją (utrata precyzji).

Przed standardem IEEE takie warunki zwykle powodowały zakończenie programu lub wyzwalały jakąś pułapkę, którą programista mógł przechwycić. Sposób działania był zależny od systemu, co oznacza, że ​​programy zmiennoprzecinkowe nie były przenośne . (Termin „wyjątek” używany w IEEE 754 jest ogólnym terminem oznaczającym wyjątkowy stan, który niekoniecznie jest błędem i jest innym użyciem niż typowo definiowany w językach programowania, takich jak C++ lub Java, w których „ wyjątek „jest alternatywnym przepływem kontroli, bliższym temu, co określa się mianem „pułapki” w terminologii IEEE 754.)

Tutaj omawiana jest wymagana domyślna metoda obsługi wyjątków zgodnie z IEEE 754 (opcjonalne trapping IEEE 754 i inne tryby „alternatywnej obsługi wyjątków” nie są omawiane). Wyjątki arytmetyczne muszą (domyślnie) być rejestrowane w „lepkich” bitach flagi stanu. To, że są „lepkie”, oznacza, że ​​nie są resetowane przez następną (arytmetyczną) operację, ale pozostają ustawione aż do jawnego zresetowania. Użycie „lepkich” flag umożliwia zatem testowanie wyjątkowych warunków, które można opóźnić aż do pełnego wyrażenia zmiennoprzecinkowego lub podprogramu: bez nich wyjątkowe warunki, których nie można w inny sposób zignorować, wymagałyby jawnego testowania natychmiast po każdej operacji zmiennoprzecinkowej. Domyślnie operacja zawsze zwraca wynik zgodny ze specyfikacją bez przerywania obliczeń. Na przykład 1/0 zwraca +∞, jednocześnie ustawiając bit flagi dzielenia przez zero (domyślna wartość ∞ jest tak zaprojektowana, aby często zwracać skończony wynik, gdy jest używany w kolejnych operacjach, a więc być bezpiecznie ignorowany).

Pierwotny standard IEEE 754 nie zalecał jednak operacji obsługujących takie zestawy bitów flagi wyjątków arytmetycznych. Więc chociaż były one implementowane sprzętowo, początkowo implementacje języka programowania zazwyczaj nie zapewniały dostępu do nich (poza asemblerem). Z biegiem czasu niektóre standardy języków programowania (np. C99 /C11 i Fortran) zostały zaktualizowane w celu określenia metod dostępu i zmiany bitów flagi stanu. Wersja 2008 standardu IEEE 754 określa teraz kilka operacji dla dostępu i obsługi bitów flag arytmetycznych. Model programowania opiera się na pojedynczym wątku wykonania i użycie ich przez wiele wątków musi być obsługiwane w sposób spoza standardu (np. C11 określa, że ​​flagi mają pamięć lokalną wątku ).

IEEE 754 określa pięć wyjątków arytmetycznych, które mają być rejestrowane we flagach stanu ("lepkie bity"):

  • inexact , ustawiany, jeśli zaokrąglona (i zwrócona) wartość różni się od matematycznie dokładnego wyniku operacji.
  • niedomiar , ustawiany, jeśli zaokrąglona wartość jest niewielka (zgodnie ze specyfikacją IEEE 754) i niedokładna (lub może ograniczona do utraty denormalizacji, zgodnie z wersją IEEE 754) z 1984 r., zwracając wartość podnormalną zawierającą zera.
  • overflow , ustaw, jeśli wartość bezwzględna zaokrąglonej wartości jest zbyt duża, aby można ją było przedstawić. Zwracana jest nieskończoność lub maksymalna skończona wartość, w zależności od zastosowanego zaokrąglenia.
  • dziel przez zero , ustawiany, jeśli wynik jest nieskończony, biorąc pod uwagę skończone operandy, zwraca nieskończoność, albo +∞ lub −∞.
  • nieważne , ustawiane, jeśli nie można zwrócić wyniku o wartości rzeczywistej, np. sqrt(−1) lub 0/0, zwracając cichy NaN.
Rys. 1: rezystancje równoległe, z rezystancją całkowitą

Domyślna wartość zwracana dla każdego z wyjątków została zaprojektowana tak, aby w większości przypadków dać poprawny wynik, tak aby wyjątki można było zignorować w większości kodów. inexact zwraca prawidłowo zaokrąglony wynik, a niedomiar zwraca zdenormalizowaną małą wartość i dlatego prawie zawsze można go zignorować. dzielenie przez zero zwraca dokładnie nieskończoność, która zwykle dzieli skończoną liczbę, a więc daje zero, albo daje niepoprawny wyjątek później, jeśli nie, i dlatego też może być zwykle zignorowana. Na przykład efektywna rezystancja n rezystorów równolegle (patrz rys. 1) jest dana wzorem . Jeśli zwarcie rozwinie się z ustawionym na 0, zwróci +nieskończoność, co da wynik końcowy równy 0, zgodnie z oczekiwaniami (patrz dalszy przykład ułamka uzasadnienia projektowego IEEE 754 dla innego przykładu).

Przepełnienia i nieprawidłowe wyjątki zazwyczaj nie mogą być ignorowane, ale niekoniecznie reprezentują błędy: na przykład procedura znajdowania korzeni , w ramach normalnego działania, może oceniać przekazaną funkcję w wartościach spoza jej domeny, zwracając NaN i nieważny flag wyjątkiem są ignorowane aż do znalezienia użyteczny punkt początkowy.

Problemy z dokładnością

Fakt, że liczby zmiennoprzecinkowe nie mogą dokładnie reprezentować wszystkich liczb rzeczywistych, a operacje zmiennoprzecinkowe nie mogą dokładnie reprezentować prawdziwych operacji arytmetycznych, prowadzi do wielu zaskakujących sytuacji. Wiąże się to ze skończoną precyzją, z jaką komputery generalnie przedstawiają liczby.

Na przykład niereprezentatywność 0,1 i 0,01 (w systemie binarnym) oznacza, że ​​wynik próby podniesienia do kwadratu 0,1 nie jest ani 0,01, ani najbliższą reprezentowalną liczbą. W reprezentacji 24-bitowej (o pojedynczej precyzji), 0,1 (dziesiętne) podano wcześniej jako e  = -4 ; s  = 110011001100110011001101 , czyli

0.100000001490116119384765625 dokładnie.

Kwadratura tej liczby daje

0.010000000298023226097399174250313080847263336181640625 dokładnie.

Kwadratura za pomocą osprzętu zmiennoprzecinkowego o pojedynczej precyzji (z zaokrągleniem) daje

0.010000000707805156707763671875 dokładnie.

Ale reprezentowalna liczba najbliższa 0,01 to

0.009999999776482582092285156250 dokładnie.

Ponadto, niereprezentowalność π (i π/2) oznacza, że ​​próba obliczenia tan(π/2) nie da wyniku nieskończoności, ani nawet nie przepełni się w zwykłych formatach zmiennoprzecinkowych (zakładając, że dokładny wdrożenie opalenizny). Po prostu nie jest możliwe, aby standardowy sprzęt zmiennoprzecinkowy próbował obliczyć tan(π/2), ponieważ π/2 nie może być dokładnie reprezentowane. To obliczenie w C:

/* Enough digits to be sure we get the correct approximation. */
double pi = 3.1415926535897932384626433832795;
double z = tan(pi/2.0);

da wynik 16331239353195370.0. W pojedynczej precyzji (przy użyciu tanffunkcji) wynik wyniesie −22877332.0.

Z tego samego powodu próba obliczenia sin(π) nie da zera. Wynik będzie (w przybliżeniu) 0,1225 x 10 -15 w podwójnej precyzji lub -0,8742 x 10 -7 w pojedynczej precyzji.

Chociaż dodawanie i mnożenie zmiennoprzecinkowe są przemienne ( a + b = b + a i a × b = b × a ), niekoniecznie są asocjacyjne . Oznacza to, że ( a + b ) + c niekoniecznie jest równe a + ( b + c ) . Używając 7-cyfrowej arytmetyki znaczącej i dziesiętnej:

 a = 1234.567, b = 45.67834, c = 0.0004
 (a + b) + c:
     1234.567   (a)
   +   45.67834 (b)
   ____________
     1280.24534   rounds to   1280.245
    1280.245  (a + b)
   +   0.0004 (c)
   ____________
    1280.2454   rounds to   1280.245  ← (a + b) + c
 a + (b + c):
   45.67834 (b)
 +  0.0004  (c)
 ____________
   45.67874
   1234.567   (a)
 +   45.67874   (b + c)
 ____________
   1280.24574   rounds to   1280.246 ← a + (b + c)

Niekoniecznie są one również dystrybucyjne . Oznacza to, że ( a + b ) × c może nie być tym samym co a × c + b × c :

 1234.567 × 3.333333 = 4115.223
 1.234567 × 3.333333 = 4.115223
                       4115.223 + 4.115223 = 4119.338
 but
 1234.567 + 1.234567 = 1235.802
                       1235.802 × 3.333333 = 4119.340

Oprócz utraty znaczenia, niemożności dokładnego przedstawienia liczb, takich jak π i 0,1 oraz innych drobnych niedokładności, mogą wystąpić następujące zjawiska:

  • Anulowanie : odejmowanie prawie równych argumentów może spowodować ekstremalną utratę dokładności. Kiedy odejmujemy dwie prawie równe liczby, ustawiamy najbardziej znaczące cyfry na zero, pozostawiając sobie tylko nieistotne i najbardziej błędne cyfry. Na przykład przy wyznaczaniu pochodnej funkcji stosuje się następujący wzór:

    Intuicyjnie chciałoby się h bardzo bliskie zeru; jednak przy użyciu operacji zmiennoprzecinkowych najmniejsza liczba nie da najlepszego przybliżenia pochodnej. Gdy h maleje, różnica między f ( a + h ) i f ( a ) maleje, eliminując najbardziej znaczące i najmniej błędne cyfry i czyniąc najbardziej błędne cyfry ważniejsze. W rezultacie najmniejsza możliwa liczba h da bardziej błędne przybliżenie pochodnej niż nieco większa liczba. Jest to prawdopodobnie najczęstszy i najpoważniejszy problem z dokładnością.
  • Konwersje na liczby całkowite nie są intuicyjne: konwersja (63,0/9,0) na liczbę całkowitą daje 7, ale konwersja (0,63/0,09) może dać 6. Dzieje się tak, ponieważ konwersje zazwyczaj są skracane, a nie zaokrąglane. Funkcje podłogi i sufitu mogą dawać odpowiedzi, które różnią się o jeden od intuicyjnie oczekiwanej wartości.
  • Ograniczony zakres wykładników: wyniki mogą przekroczyć, dając nieskończoność, lub niedomiar, dając liczbę podnormalną lub zero. W takich przypadkach precyzja zostanie utracona.
  • Testowanie bezpiecznego dzielenia jest problematyczne: Sprawdzenie, czy dzielnik nie jest równy zero, nie gwarantuje, że dzielenie nie zostanie przepełnione.
  • Testowanie równości jest problematyczne. Dwie sekwencje obliczeniowe, które są matematycznie równe, mogą równie dobrze dawać różne wartości zmiennoprzecinkowe.

Incydenty

Precyzja maszyny i analiza błędów wstecznych

Precyzja maszyny jest wielkością charakteryzującą dokładność systemu zmiennoprzecinkowego i jest wykorzystywana w analizie błędów wstecznych algorytmów zmiennoprzecinkowych. Jest również znany jako zaokrąglenie jednostkowe lub epsilon maszynowy . Zwykle oznaczany jako Ε mach , jego wartość zależy od konkretnego zastosowanego zaokrąglenia.

Z zaokrągleniem do zera,

natomiast zaokrąglanie do najbliższego,

Jest to ważne, ponieważ ogranicza względny błąd w reprezentowaniu dowolnej niezerowej liczby rzeczywistej x w znormalizowanym zakresie systemu zmiennoprzecinkowego:

Analiza błędów wstecznych, której teorię opracował i spopularyzował James H. Wilkinson , może posłużyć do ustalenia, czy algorytm realizujący funkcję numeryczną jest stabilny numerycznie. Podstawowym podejściem jest pokazanie, że chociaż obliczony wynik, z powodu błędów zaokrągleń, nie będzie dokładnie poprawny, jest to dokładne rozwiązanie pobliskiego problemu z lekko zaburzonymi danymi wejściowymi. Jeśli wymagana perturbacja jest niewielka, rzędu niepewności danych wejściowych, to wyniki są w pewnym sensie tak dokładne, jak dane „zasługują”. Algorytm jest wtedy określany jako stabilny wstecznie . Stabilność jest miarą wrażliwości na błędy zaokrągleń danej procedury numerycznej; z kolei numer warunku funkcji dla danego problemu wskazuje na wrodzoną wrażliwość funkcji na małe perturbacje w jej danych wejściowych i jest niezależny od implementacji zastosowanej do rozwiązania problemu.

Jako trywialny przykład rozważ proste wyrażenie dające iloczyn skalarny (długości dwa) wektorów i , wtedy

a więc

gdzie

gdzie

z definicji, który jest sumą dwóch lekko zaburzonych (rzędu Ε mach ) danych wejściowych, a więc jest stabilny wstecznie. Aby uzyskać bardziej realistyczne przykłady w numerycznej algebrze liniowej , zobacz Higham 2002 i inne odnośniki poniżej.

Minimalizowanie wpływu problemów z dokładnością

Chociaż, jak zauważono wcześniej, poszczególne operacje arytmetyczne IEEE 754 mają gwarantowaną dokładność z dokładnością do połowy ULP, bardziej skomplikowane formuły mogą zawierać większe błędy z powodu zaokrągleń. Utrata dokładności może być znaczna, jeśli problem lub jego dane są źle uwarunkowane , co oznacza, że ​​poprawny wynik jest nadwrażliwy na drobne zakłócenia w jego danych. Jednak nawet funkcje, które są dobrze uwarunkowane, mogą ucierpieć z powodu dużej utraty dokładności, jeśli zostanie użyty algorytm niestabilny numerycznie dla tych danych: pozornie równoważne sformułowania wyrażeń w języku programowania mogą się znacznie różnić pod względem stabilności numerycznej. Jednym ze sposobów usunięcia ryzyka takiej utraty dokładności jest projektowanie i analiza algorytmów stabilnych numerycznie, co jest celem dziedziny matematyki zwanej analizą numeryczną . Innym podejściem, które może chronić przed ryzykiem niestabilności liczbowych, jest obliczanie wartości pośrednich (zarysowania) w algorytmie z większą precyzją niż wymaga końcowy wynik, co może usunąć lub zmniejszyć o rzędy wielkości takie ryzyko: poczwórne IEEE 754 precyzja i rozszerzona precyzja są przeznaczone do tego celu podczas obliczeń z podwójną precyzją.

Na przykład poniższy algorytm jest bezpośrednią implementacją obliczania funkcji A ( x ) = ( x −1) / (exp( x −1) − 1), która jest dobrze uwarunkowana przy 1.0, jednak można wykazać, że jest numerycznie niestabilne i tracą do połowy cyfr znaczących przenoszonych przez arytmetykę, gdy są obliczane w pobliżu 1,0.

double A(double X)
{
        double Y, Z;  // [1]
        Y = X - 1.0;
        Z = exp(Y);
        if (Z != 1.0)
                Z = Y / (Z - 1.0); // [2]
        return Z;
}

Jeśli jednak wszystkie obliczenia pośrednie są wykonywane z rozszerzoną dokładnością (np. przez ustawienie linii [1] na C99 long double ), to można zachować pełną precyzję w końcowym podwójnym wyniku. Alternatywnie analiza numeryczna algorytmu pokazuje, że w przypadku następującej nieoczywistej zmiany linii [2]:

Z = log(Z) / (Z - 1.0);

wtedy algorytm staje się numerycznie stabilny i może obliczyć z pełną podwójną precyzją.

Aby zachować właściwości tak starannie skonstruowanych, stabilnych numerycznie programów, kompilator wymaga ostrożnego obchodzenia się z nimi . Pewne „optymalizacje”, które mogą dokonywać kompilatory (na przykład zmiana kolejności operacji), mogą działać wbrew celom dobrze zachowującego się oprogramowania. Istnieją pewne kontrowersje dotyczące błędów kompilatorów i projektów językowych w tej dziedzinie: C99 jest przykładem języka, w którym takie optymalizacje są starannie określone, aby zachować precyzję numeryczną. Zobacz odnośniki zewnętrzne na dole tego artykułu.

Szczegółowe omówienie technik pisania wysokiej jakości oprogramowania zmiennoprzecinkowego wykracza poza zakres tego artykułu i odsyłam do czytelnika i innych odnośników na końcu tego artykułu. Kahan sugeruje kilka praktycznych zasad, które mogą znacznie zmniejszyć o rzędy wielkości ryzyko anomalii numerycznych, w uzupełnieniu lub w miejsce dokładniejszej analizy numerycznej. Obejmują one: jak wspomniano powyżej, obliczanie wszystkich wyrażeń i wyników pośrednich z najwyższą precyzją obsługiwaną przez sprzęt (powszechną praktyczną zasadą jest noszenie podwójnej precyzji pożądanego wyniku, tj. obliczanie z podwójną precyzją dla końcowego wyniku z pojedynczą precyzją, lub z podwójną dokładnością lub poczwórną precyzją dla wyników o podwójnej precyzji); oraz zaokrąglanie danych wejściowych i wyników tylko do precyzji wymaganej i obsługiwanej przez dane wejściowe (przewyższanie precyzji wyniku końcowego poza wymaganą i obsługiwaną przez dane wejściowe może wprowadzać w błąd, zwiększać koszt przechowywania i zmniejszać prędkość, a nadmiarowe bity mogą wpływają na zbieżność procedur numerycznych: w szczególności pierwsza forma przykładu iteracyjnego podanego poniżej jest zbieżna poprawnie przy użyciu tej reguły kciuka). Poniżej znajdują się krótkie opisy kilku dodatkowych zagadnień i technik.

Ponieważ ułamki dziesiętne często nie mogą byćdokładnie reprezentowane w binarnych liczbach zmiennoprzecinkowych, taka arytmetyka jest najlepsza, gdy jest po prostu używana do mierzenia rzeczywistych wielkości w szerokim zakresie skal (takich jak okres orbitalny księżyca wokół Saturna lub masa protonu ), a w najgorszym przypadku, gdy oczekuje się modelowania interakcji wielkości wyrażonych jako ciągi dziesiętne, które mają być dokładne. Przykładem tego drugiego przypadku są obliczenia finansowe. Z tego powodu oprogramowanie finansowe zwykle nie używa binarnej reprezentacji liczb zmiennoprzecinkowych. „Dziesiętny” typ danych języków programowania C# i Python oraz formaty dziesiętne standardu IEEE 754-2008 zostały zaprojektowane w celu uniknięcia problemów związanych z binarnymi reprezentacjami zmiennoprzecinkowymi w przypadku zastosowania do dokładnych wartości dziesiętnych wprowadzanych przez człowieka oraz arytmetyka zawsze zachowuje się zgodnie z oczekiwaniami, gdy liczby są drukowane w postaci dziesiętnej.

Oczekiwania matematyczne mogą nie być realizowane w dziedzinie obliczeń zmiennoprzecinkowych. Na przykład wiadomo, że , i że , jednak nie można polegać na tych faktach, gdy zaangażowane wielkości są wynikiem obliczeń zmiennoprzecinkowych.

Użycie testu równości ( if (x==y) ...) wymaga ostrożności podczas pracy z liczbami zmiennoprzecinkowymi. Nawet proste wyrażenia, takie jak 0.6/0.2-3==0will, na większości komputerów nie są prawdziwe (na przykład w IEEE 754 podwójna precyzja 0.6/0.2 - 3jest w przybliżeniu równa -4,44089209850063e-16). W konsekwencji takie testy są czasami zastępowane porównaniami „rozmytymi” ( if (abs(x-y) < epsilon) ..., gdzie epsilon jest wystarczająco mały i dostosowany do aplikacji, np. 1.0E−13). Mądrość robienia tego jest bardzo zróżnicowana i może wymagać analizy numerycznej, aby związać epsilon. Wartości pochodzące z reprezentacji danych pierwotnych i ich porównania powinny być wykonywane z szerszą, rozszerzoną precyzją, aby zminimalizować ryzyko takich niezgodności z powodu błędów zaokrągleń. Często lepiej jest zorganizować kod w taki sposób, aby takie testy były niepotrzebne. Na przykład w geometrii obliczeniowej dokładne testy tego, czy punkt leży poza linią lub płaszczyzną zdefiniowaną przez inne punkty, można przeprowadzić przy użyciu precyzji adaptacyjnej lub dokładnych metod arytmetycznych.

Małe błędy w arytmetyce zmiennoprzecinkowej mogą narastać, gdy algorytmy matematyczne wykonują operacje ogromną liczbę razy. Kilka przykładów to inwersja macierzy , obliczanie wektora własnego i rozwiązywanie równań różniczkowych. Algorytmy te muszą być bardzo starannie zaprojektowane przy użyciu metod numerycznych, takich jak iteracyjne udoskonalanie , jeśli mają działać dobrze.

Sumowanie wektora wartości zmiennoprzecinkowych jest podstawowym algorytmem w obliczeniach naukowych , dlatego świadomość tego, kiedy może nastąpić utrata znaczenia, jest niezbędna. Na przykład, jeśli dodaje się bardzo dużą liczbę liczb, poszczególne dodatki są bardzo małe w porównaniu z sumą. Może to prowadzić do utraty znaczenia. Typowym dodatkiem byłoby wtedy coś takiego

3253.671
+  3.141276
-----------
3256.812

Niskie 3 cyfry dodatków są skutecznie tracone. Załóżmy na przykład, że trzeba dodać wiele liczb, wszystkie w przybliżeniu równe 3. Po dodaniu 1000 z nich suma bieżąca wynosi około 3000; utracone cyfry nie są odzyskiwane. Do zmniejszenia błędów można użyć algorytmu sumowania Kahana .

Błąd zaokrąglenia może wpływać na zbieżność i dokładność iteracyjnych procedur numerycznych. Jako przykład, Archimedes aproksymował π, obliczając obwody wielokątów wpisujących i opisujących okrąg, zaczynając od sześciokątów i kolejno podwajając liczbę boków. Jak wspomniano powyżej, obliczenia mogą być uporządkowane w sposób, który jest matematycznie równoważny, ale mniej podatny na błędy ( analiza numeryczna ). Dwie formy wzoru rekurencyjnego dla opisanego wielokąta to:

  • Pierwsza forma:
  • druga forma:
  • , zbieżne jako

Oto obliczenia przy użyciu arytmetyki IEEE „podwójnej” (znaczącej z 53 bitami precyzji):

 i   6 × 2i × ti, first form    6 × 2i × ti, second form
---------------------------------------------------------
 0   3.4641016151377543863      3.4641016151377543863
 1   3.2153903091734710173      3.2153903091734723496
 2   3.1596599420974940120      3.1596599420975006733
 3   3.1460862151314012979      3.1460862151314352708
 4   3.1427145996453136334      3.1427145996453689225
 5   3.1418730499801259536      3.1418730499798241950
 6   3.1416627470548084133      3.1416627470568494473
 7   3.1416101765997805905      3.1416101766046906629
 8   3.1415970343230776862      3.1415970343215275928
 9   3.1415937488171150615      3.1415937487713536668
10   3.1415929278733740748      3.1415929273850979885
11   3.1415927256228504127      3.1415927220386148377
12   3.1415926717412858693      3.1415926707019992125
13   3.1415926189011456060      3.1415926578678454728
14   3.1415926717412858693      3.1415926546593073709
15   3.1415919358822321783      3.1415926538571730119
16   3.1415926717412858693      3.1415926536566394222
17   3.1415810075796233302      3.1415926536065061913
18   3.1415926717412858693      3.1415926535939728836
19   3.1414061547378810956      3.1415926535908393901
20   3.1405434924008406305      3.1415926535900560168
21   3.1400068646912273617      3.1415926535898608396
22   3.1349453756585929919      3.1415926535898122118
23   3.1400068646912273617      3.1415926535897995552
24   3.2245152435345525443      3.1415926535897968907
25                              3.1415926535897962246
26                              3.1415926535897962246
27                              3.1415926535897962246
28                              3.1415926535897962246
              The true value is 3.14159265358979323846264338327...

Podczas gdy dwie formy wzoru powtarzania są wyraźnie matematycznie równoważne, pierwsza odejmuje 1 od liczby bardzo bliskiej 1, co prowadzi do coraz bardziej problematycznej utraty cyfr znaczących . Ponieważ powtarzalność jest stosowana wielokrotnie, najpierw poprawia się dokładność, ale potem się pogarsza. Nigdy nie jest lepsza niż około 8 cyfr, mimo że 53-bitowa arytmetyka powinna mieć około 16 cyfr precyzji. Gdy używana jest druga forma cyklu, wartość zbiega się z dokładnością 15 cyfr.

Optymalizacja „szybkiej matematyki”

Wspomniany wcześniej brak asocjatywności operacji zmiennoprzecinkowych ogólnie oznacza, że kompilatory nie mogą tak efektywnie zmieniać kolejności wyrażeń arytmetycznych, jak mogłyby w przypadku arytmetyki liczb całkowitych i stałoprzecinkowych, co stanowi przeszkodę w optymalizacji, takich jak eliminacja wspólnych podwyrażeń i autowektoryzacja . Opcja „szybkiej matematyki” w wielu kompilatorach (ICC, GCC, Clang, MSVC...) włącza ponowne skojarzenie wraz z niebezpiecznymi założeniami, takimi jak brak NaN i nieskończone liczby w IEEE 754. Niektóre kompilatory oferują również bardziej szczegółowe opcje tylko włącz ponowne skojarzenie. W obu przypadkach programista jest narażony na wiele pułapek związanych z precyzją wspomnianych powyżej dla części programu korzystającej z "szybkiej" matematyki.

W niektórych kompilatorach (GCC i Clang) włączenie „szybkiej” matematyki może spowodować, że program wyłączy podnormalne pływaki podczas uruchamiania, wpływając na zachowanie zmiennoprzecinkowe nie tylko wygenerowanego kodu, ale także dowolnego programu korzystającego z takiego kodu jako biblioteki .

W większości kompilatorów Fortran , zgodnie z normą ISO/IEC 1539-1:2004 Fortran, ponowne skojarzenie jest ustawieniem domyślnym, a awariom w dużej mierze zapobiega ustawienie „protect parens” (również włączone domyślnie). To ustawienie uniemożliwia kompilatorowi ponowne skojarzenie poza granice nawiasów. Kompilator Intel Fortran jest godnym uwagi odstaniem.

Częstym problemem w "szybkiej" matematyce jest to, że podwyrażenia mogą nie być zoptymalizowane identycznie z miejsca na miejsce, co prowadzi do nieoczekiwanych różnic. Jedną z interpretacji problemu jest to, że „szybka” matematyka w obecnej formie ma słabo zdefiniowaną semantykę. Jedną z prób sformalizowania „szybkich” optymalizacji matematycznych jest Icing , zweryfikowany kompilator.

Zobacz też

Uwagi

Bibliografia

Dalsza lektura

Zewnętrzne linki