Laboratorium 4

Laboratorium 4: Arytmetyka zmiennoprzecinkowa komputerów

Wstęp

Te zajęcia nawiązują do sposobu zapisu liczb opisanego w „części teoretycznej”. Ich celem jest odświeżenie tej wiedzy, zaprojektowanie prostego eksperymentu pozwalającego sprawdzić jak obliczenia są wykonywane (zwłaszcza w arkuszu kalkulacyjnym).

Druga część ma pokazać w jaki sposób można te ograniczenia omijać korzystając ze specjalnych bibliotek.

Proste obliczenia

Jest taki program [1]1

nie prowadzi on żadnych skomplikowanych obliczeń przeprowadzając jedynie proste działanie: \[x_{i+1}=\alpha x_i(1-x_i)\] gdzie \(\alpha=3.8\), a \(x_0=0.5\).

Wykonuje on obliczenia na liczbach typu float (32 bity), double (64 bity) i long double (128 bitów). Program wykonuje 100 iteracji drukując co dziesiątą z nich. Wyniki programu (można go łatwo zapisać na dysku jako caos.c i skompilować poleceniem gcc -o caos caos.c.

Wyniki wyglądają jakoś tak:

Jak widać rezultaty różnią się w sposób znaczący. Jedynym wytłumaczeniem jest różna liczba bitów uwzględnianych w obliczeniach. Zatem przypuszczać można że wersja 128-bitowa jest najbliższa rzeczywistości.

Jeżeli wierzyć rozważaniom z [1] „poprawne wyniki” (uzyskane w arytmetyce o 1000 cyfr) są następujące:

i jak widać odrobinę różnią się od najlepszego uzyskanego wyniku.

Okazuje się, że dla \(0\le \alpha < 3\) zachowanie obliczeń jest deterministyczne, a dla \(3 \le \alpha <4\) — chaotyczne. Takie systemy (chaotyczne) są bardzo czułe na dokładność obliczeń.

Mathematica

Mathematica dla każdego obliczenia potrafi podać precyzję wyniku. Służy do tego funkcja Precision[]. Zazwyczaj obliczenia wykonywane są z „maszynową precyzją“ (co oznacza obliczenia na liczbach podwójnej precyzji (64 bity).

Dodatkowo można zadeklarować w jakiej precyzji mają być wykonywane obliczenia.

Znalazłem też instrukcję (której do końca nie rozumiem) powodującą, że wszystkie obliczenia w notatniku będą odbywać się z zadaną precyzją. Wygląda ona jakoś tak:

i powinna być umieszczona na początku notatnika2. Precyzja określona jest stałą tekstową na samym końcu polecenia ("`1000."). Demonstruje to prosty przykład (notatnik Mathematici).

Powinno to wystarczyć do zaprogramowania opisywanego przykładu.

Mathematica to pełnowymiarowy język programowania, wyposażony w instrukcje warunkowe (If czy służące do tworzenia pętli (For, Do, While). Prosty przykład programiku z tymi instrukcjami zawiera notatnik.

Jak ktoś ciągle ma kłopoty może skorzystać z kolejnego przykładowego programu.

Pakiet Computer Arithmetics

Mathematica wyposażona jest w pakiet Computer Aruithmetics służący do symulowania obliczeń korzystając z jakiejś „wymyślonej” arytmetyki komputerowe. Pozwala to badać jaki wpływ na na precyzję obliczeń może mieć liczba dostępnych bitów czy zakres zmian wartości wykładnika.

W szczególności pakiet pozwala na symulowanie komputerów o arytmetyce dziesiętnej (nie binarnej). Wydaje się że do prowadzenia złożonych obliczeń inżynierskich taki rodzaj arytmetyki może być lepszy niż stosowana dziś arytmetyka binarna.

Aby z pakietu skorzystać, trzeba go załadować. Służy d tego polecenie iNeeds.

Następnie definiujemy rodzaj arytmetyki poleceniem SetArithmetic[d,b] gdzie d to liczba używanych cyfr (niestety z zakresu 1 do 10), a b to podstawa systemu liczenia z zakresu od 2 do 16. Nie można zatem poszaleć z liczbą używanych w obliczeniach cyfr (co utrudnia nieco nasze zadanie).

Kolejne polecenie to ComputerNumber definiujące obiekt w wybranej arytmetyce:

(Standardowo nie można wykonywać obliczeń na liczbach mieszanych więc trzeba uważać na zapis:

gdyż jedynka jest z innego świata (obiektem innego typu). Czyli powyższe trzeba zapisać jako:

albo

Takie obliczenia również można wykonać aby sprawdzić zachowanie naszego problemu. Od razu podpowiadam, że można wykorzystać arytmetykę szesnastkową o 10 cyfrach (uzyskamy większą precyzję niż w przypadku arytmetyki 10 o 10 cyfrach). Ale i tak to za mało.

Python

Język programowania python może być wyposażony w bibliotekę mpmath ([2]), pozwalającą na obliczenia w dowolnej precyzji.

Korzystanie z niej jest stosunkowo proste. Trzeba ją najpierw „załadować”

Polecenia mp.precmp.dps definiują precyzję obliczeń. Pierwsze określa ją w bitach, drugie w cyfrach dziesiętnych. Wystarczy korzystać tylko z jednego, gdyż ich wartości zależne są od siebie: \(\text{prec}\sim 3.33 \text{ dps}\)

Największy kłopot jest z deklaracją stałych, które nie mogą być standardowymi literałami.

Wprowadzając dane trzeba pamiętać o błędach konwersji z układu dziesiętnego do dwójkowego. W związku z tym spodziewać można się dziwnych zjawisk

w pierwszym przypadku najpierw robiona jest konwersja z liczby dziesiętnej do binarnej, a później wynik traktowany jest jako liczba wielokrotnej precyzji. W drugim wypadku do funkcji mpf przekazujemy tekst, który jest interpretowany i konwertowany od razu na liczbę wielokrotnej precyzji.

Reszta jest już stosunkowo prosta.

  1. Dodawanie: fadd()
  2. Odejmowanie: fsub()
  3. Negacja (zmiana znaku): fneg()
  4. Iloczyn: fmul()
  5. Iloraz: fdiv()

Chociaż jak już zmienne zostały „zadeklarowane” jako dużej precyzji można korzystać ze standardowych operatorów +-/*.

Idle

Można ułatwić sobie pracę w pythonie uruchamiając proste środowisko graficzne zwane idle. Pozwala ono zapisać program korzystając z prostego edytora i bardzo łatwo uruchamiać go.

Jupyter

Znacznie bardziej wygodne może być skorzystanie z notatnika Jupyter. Przygotowałem odpowiedni przykład, który można
pobrać (wraz z innymi) stąd.

Zadania do wykonania

  1. Zaproponować eksperyment obliczeniowy wyznaczający liczbę bitów i dokładność prowadzonych obliczeń.
  2. Przetestować go na matlabie i Mathematici, w arkuszu kalkulacyjnym LibreOffice calc, Blockly i — jeżeli ktoś konto Google posiada dla arkusza kalkulacyjnego Google (i ewentualnie dla Microsoft Office Online).
  3. Pobawić się w pythonie3 i Mathematici prowadzeniem obliczeń w dużej precyzji programując podany na początku przykład i porównując (jakieś wykresy?) wyniki uzyskane dla różnych precyzji obliczeń.

Instrukcja w postaci jednego pliku…

…jest również dostępna.


  1. Problem pojawił się po raz pierwszy podczas prób symulacji izolowanej populacji insektów.
  2. Ustalona precyzja będzie obowiązywała we wszystkich równocześnie otwartych notebookach
  3. Programowanie w pythonie jest stosunkowo proste ([3], [4], [5], [6]). Można również zerknąć do instrukcji laboratoryjnych do zajęć z Technologii Informacyjnych. Na stronach TI dla informatykji znajduje się również obszerny spis literatury. Natomiast, jeżeli ktoś boi się zaczynać, powinien zerknąc na przygotowaną stronę w Blockly zawierający program i pozwalającą na podejrzenie wersji skonwertowanej do pythona. Skopiować z tego okienka jest trudno, ale się da. Klikamy w nie i w ciemno Ctrl-A, Ctrl-C i zawartość wklejamy do edytora. Później trzeba tylko jeszcze podzielić na linie (i zadbać o właściwe wcięcia, ale jak robić to ostrożne – udaje się).

  • A. Ward, „Some issues on floating-point precision under linux,” Linux gazette, iss. 53, 2000.
    [BibTeX] [Download PDF]
    @Article{Ward2000,
    author = {Alan Ward},
    title = {Some issues on floating-point precision under Linux},
    journal = {Linux Gazette},
    year = {2000},
    number = {53},
    url = {https://linuxgazette.net/53/ward.html},
    month = {May},
    owner = {myszka},
    timestamp = {2012.10.15},
    }

  • F. Johansson, „mpmath’s documentation,” , 2015.
    [BibTeX] [Download PDF]
    @Electronic{Johansson2015,
    author = {Fredrik Johansson},
    title = {{mpmath}’s documentation},
    year = {2015},
    url = {http://mpmath.org/doc/current/index.html},
    owner = {myszka},
    timestamp = {2015.11.05},
    }

  • M. Pilgrim, Zanurkuj w pythonie, WikiBooks, 2010.
    [BibTeX] [Abstract] [Download PDF]

    Niniejszy podręcznik powstaje na podstawie książki Dive into Python Marka Pilgrima (w większości jest to tłumaczenie) udostępnionej na licencji {GNU} Free Documentation License. Kody wszystkich przykładów można pobrać stąd.

    @Book{mark-pilgrim-zanurkuj-2010,
    author = {Mark Pilgrim},
    title = {Zanurkuj w Pythonie},
    year = {2010},
    publisher = {{WikiBooks}},
    url = {http://pl.wikibooks.org/wiki/Python},
    abstract = {Niniejszy podręcznik powstaje na podstawie książki Dive into Python Marka Pilgrima (w większości jest to tłumaczenie) udostępnionej na licencji {GNU} Free Documentation License. Kody wszystkich przykładów można pobrać stąd.},
    owner = {myszka},
    timestamp = {2010.10.27},
    }

  • Python documentation index, 2010.
    [BibTeX] [Download PDF]
    @Misc{python-2010,
    title = {Python documentation index},
    year = {2010},
    howpublished = {\url{http://www.python.org/doc/}},
    month = sep,
    url = {http://www.python.org/doc/},
    key = {Python},
    owner = {myszka},
    timestamp = {2015.11.06},
    }

  • Z. A. Shaw, Learn python the hard way, , 2010.
    [BibTeX] [Download PDF]
    @Book{Shaw2010,
    author = {Zed A. Shaw},
    title = {Learn Python The Hard Way},
    year = {2010},
    url = {http://learnpythonthehardway.org/book/},
    owner = {myszka},
    timestamp = {2010.10.04},
    }

  • Python programming, WikiBooks, 2010.
    [BibTeX] [Abstract] [Download PDF]

    Python is a general-purpose interpreted programming language. It currently has distributions available for Microsoft Windows, Apple Mac {OS} X, {GNU/Linux,} {BSD,} and many other platforms. There are currently three major implementations: the standard implementation written in C, Jython written in Java, and {IronPython} written in C\# for the {MS} {.NET} environment.

    @Book{wiki-python-2010,
    title = {Python Programming},
    year = {2010},
    publisher = {{WikiBooks}},
    url = {http://en.wikibooks.org/wiki/Python_Programming},
    abstract = {Python is a general-purpose interpreted programming language. It currently has distributions available for Microsoft Windows, Apple Mac {OS} X, {GNU/Linux,} {BSD,} and many other platforms. There are currently three major implementations: the standard implementation written in C, Jython written in Java, and {IronPython} written in C\# for the {MS} {.NET} environment.},
    key = {Python},
    owner = {myszka},
    timestamp = {2010.10.27},
    }