Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Wybrane aspekty programowania w C++ (i nie tylko) Konsekwencje zastosowania systemu binarnego do zapisu wartości liczb Czyli: dlaczego wyniki są złe, mimo że program jest napisany „dobrze”?
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Typy całkowite Zapis zmiennoprzecinkowy Typy rzeczywiste Tablice
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Typy całkowite: int „Najbardziej oczywisty” typ całkowity 4 bajty: 32 bity, 232 = 4294967296 możliwości Zakres wartości: od -2147483648 do 2147483647 Przy przekroczeniu górnego zakresu, program zaczyna liczyć od dolnego zakresu (zamiast 2147483648 będzie -2147483648) Przy przekroczeniu dolnego zakresu, program zaczyna liczyć od górnego zakresu (zamiast -2147483650 będzie 2147483646)
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Typy całkowite: int, long long int – różne standardy instrukcja „stary” standard „nowy” standard int Azja=4190000000; Uwaga, przypisanie błędnej wartości int Azja=5190000000; Błąd long long int Azja=4190000000; prawidłowa wartość (jak unsigned) prawidłowa wartość long long int Azja=5190000000; Błąd (trzeba dopisać ll)
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Typy całkowite: unsigned int, long long int Program „ludność świata” Unsigned int - zakres wartości: od 0 do 4294967295 long long (int) – 8 bajtów, 264 możliwości, liczby ujemne i dodatnie rzędu tryliona (od ok. -1018 do 1018) Przy deklarowaniu liczby o dużym module – dopisać LL (konieczne w starszym standardzie), w wyrażeniach: (long long)x, (long long)1000000 Można stosować unsigned long long
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Typy całkowite: UWAGA NA ZASADĘ: Typ wyniku taki jak typ argumentów w wyrażeniu Zaokrąglenia przy dzieleniu liczb całkowitych 8 / 3 = 2 (??) Obliczając liczbę rzeczywistą: np. 8. / 3 lub (float)8 / 3 Mamy int x=1000000, y=1000000 oraz long long z z=x*y – nie otrzymamy 1000000000000 ! (co najmniej jedna ze zmiennych x i y musi być long long)
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Typy rzeczywiste Liczba – konkretna ilość bitów Pewnych wartości nie można zapisać dokładnie używając skończonej ilości bitów (lub cyfr, np. 1/3 = 0.33333…), możliwa utrata części informacji Narastanie błędów w trakcie obliczeń Kolejność w dodawaniu – czy ma znaczenie? Warunki, pętle
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Zapis zmiennoprzecinkowy: system dziesiętny ± 1.23456 * 10 12 Wykładnik (tu: 2 miejsca ze znakiem) Znak Mantysa (tu: 6 miejsc) podstawa 3276.14 3.27614 * 10 3 -0.00005413 -5.413 * 10 -5
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Wybrane aspekty programowania w C++ (i nie tylko) Zapis zmiennoprzecinkowy: uogólnienie ± _._ _ _ _ _ _ * X _ _ _ _ Wykładnik (n+1 miejsc, w tym 1 na znak) Mantysa (m miejsc) podstawa Znak Mantysa znormalizowana: wartość między 1 a X Skończona liczba miejsc na zapis liczby: zbiór liczb, które można zapisać jest skończony! (zbiór dyskretny – tylko pewne konkretne liczby)
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Wybrane aspekty programowania w C++ (i nie tylko) Zapis zmiennoprzecinkowy: uogólnienie ± _._ _ _ _ _ _ * X _ _ _ _ Wykładnik (n+1 miejsc, w tym 1 na znak) Mantysa (m miejsc) podstawa Znak 1 / 3 = 0.33333333… 3.33333 * 10 -1 W danym systemie niektórych liczb nie można zapisać skończoną ilością cyfr: Utrata części informacji (dokładności) 1 / 3 + 1 / 3 + 1 / 3 9.99999 * 10 -1 1
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Wybrane aspekty programowania w C++ (i nie tylko) Zapis zmiennoprzecinkowy: uogólnienie ± _._ _ _ _ _ _ * X _ _ _ _ Wykładnik (n+1 miejsc, w tym 1 na znak) Mantysa (m miejsc) podstawa Znak Maksymalny wykładnik: Emax = Xn -1 (np. X = 10, n=3 Emax = 999) Minimalny wykładnik: Emin = -Xn +1 (np. X = 10, n=3 Emin = -999) Minimalna mantysa: Mmin = 1 Maksymalna mantysa: Mmax = X – X-(m-1) (np. X = 10, m=4 Mmax = 10 – 10-3 = 9.999)
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Zapis zmiennoprzecinkowy: uogólnienie ± _._ _ _ _ _ _ * X _ _ _ _ Wykładnik (n+1 miejsc, w tym 1 na znak) Mantysa (m miejsc) podstawa Znak Najmniejsza liczba dodatnia: ymin = Mmin * X Emin = 1 * X Emin Największa liczba dodatnia: ymax = Mmax * X Emax = ( X – X-(m-1) ) * X Emax Zbiór liczb: [ - ymax , - ymin ] oraz [ ymin , ymax ] (zbiór dyskretny) -ymax -ymin ymin ymax y
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Wybrane aspekty programowania w C++ (i nie tylko) Zapis zmiennoprzecinkowy: system binarny, format IEEE 754 Liczba rzeczywista w pojedynczej precyzji (float): 4 bajty = 32 bity znak wykładnik Mantysa 31 30 29 … 24 23 22 21 20 … 2 1 0 1 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 System binarny: pierwszą cyfrą mantysy jest zawsze 1 – nie trzeba poświęcać miejsca na zapisanie jedynki wartosc = (-1)0 *(1+0.5+0.25+0.125+0.0625) * 22 = 7.75
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Wybrane aspekty programowania w C++ (i nie tylko) i 2^(-i) 1 0.5 2 0.25 3 0.125 4 0.0625 5 0.03125 6 0.015625 7 0.0078125 … 23 0.00000011920928955078125 Mantysa: 23 bity. Dokładnie można wyrazić tylko te liczby, których mantysy są wielokrotnościami liczby 2-23 Dokładność zapisu liczby rzeczywistej: 7 – 8 pierwszych cyfr
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Zapis zmiennoprzecinkowy: system binarny, format IEEE 754 Pojedyncza precyzja (float): 32 bity Można przedstawiać liczby o modułach od 10-38 do 10+38 Liczby o module większym od 10+38 : bity wykładnika: 1 1 1 1 1 1 1 jeśli wszystkie bity mantysy to zera, to nieskończoność – INF, jeśli nie to NAN Liczby o module od 10-45 do 10-39 : bity wykładnika 0 0 0 0 0 0 0 0 mantysa nieunormowana – przed kropką jest 0 (mniej cyfr dokładnych) Liczby o module mniejszym od 10-45 : bity wykładnika i mantysy wyzerowane otrzymujemy +0 lub - 0
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Wybrane aspekty programowania w C++ (i nie tylko) Zapis zmiennoprzecinkowy: system binarny, format IEEE 754 y = 19.5 w formacie IEEE 754? Ustalamy, przez jaką potęgę 2 należy podzielić y, aby przed kropką była 1; dobieramy sumę potęg 2 po kropce y = 1.21875 * 24 = = (1+ 0*0.5 + 0* 0.25 + 1* 0.125 + 1*0.0625 + 1* 0.03125) * 24 znak wykładnik Mantysa 31 30 29 … 24 23 22 21 20 … 2 1 0 1 0 0 0 0 0 1 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Wybrane aspekty programowania w C++ (i nie tylko) Zapis zmiennoprzecinkowy: system binarny, format IEEE 754 przykład liczby, której nie można przedstawić dokładnie (za pomocą skończonej liczby bitów) y = 1.1 znak wykładnik Mantysa 31 30 29 … 24 23 22 21 20 … 2 1 0 0 1 1 1 1 1 1 1 0 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 1
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Wybrane aspekty programowania w C++ (i nie tylko) Zapis zmiennoprzecinkowy: system binarny, format IEEE 754 przykład dwóch liczb, które mają tę samą reprezentację y = 1.0000003; z = 1.0000004; program „float” znak wykładnik Mantysa 31 30 29 … 24 23 22 21 20 … 2 1 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Zapis zmiennoprzecinkowy: system binarny, format IEEE 754 Liczba maszynowa – może się różnić od liczby rzeczywistej Obliczenia wykonywane na liczbach „niedokładnie zapamiętanych” – wyniki obarczone niedokładnościami (mogą narastać wraz ilością operacji) Float – dokładność dla pierwszych 7 - 8 cyfr (również liczby całkowite – prawidłowo dla modułów mniejszych od 16777217, powyżej – mantysa może być za krótka, potrzeba więcej pozycji) Dodawanie liczb maszynowych nie jest przemienne, najpierw dodajemy liczby o najmniejszych modułach Niedokładna wartość liczby: wynik operacji logicznej może być inny niż oczekiwany (np. dwie wartości powinny być równe, a są różne)
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Zapis zmiennoprzecinkowy: system binarny, format IEEE 754 double – podwójna precyzja: 64 bity 52 bity na zapis mantysy, 11 bitów na wykładnik Można przedstawiać liczby o modułach od 10-308 do 10+308 Dokładność dla pierwszych 15 cyfr – nadal nie jest to dokładność idealna (pewnych problemów nie da się wyeliminować)
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Pisząc program, powinniśmy: Znać ograniczenia poszczególnych typów Mieć świadomość możliwych niedokładności wyników Czytać komunikaty o błędach i uwagi kompilatora Nie traktować wyników w sposób bezkrytyczny Testować program na różne sposoby
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Wstęp do algorytmiki (1) Problemy niedoświadczonych programistów: Przejście od pisania krótkich programów (kilka – kilkanaście linii) do implementacji algorytmów Projektowanie szczegółów implementacji Praca z plikiem źródłowym, który nie mieści się na jednym ekranie Łatwiej jest napisać program niż poszukiwać w nim błędów!
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Wstęp do algorytmiki (2) Rozwiązywanie problemów – jak jest? Wprowadzanie drobnych zmian „losowych” (nieprzemyślanych): zmieniam kawałek i sprawdzam, czy jest poprawa. Jeśli nie ma, to znów coś zmieniam i sprawdzam. Taki „ciąg” może być zbieżny do prawidłowego programu, ale metoda się nie sprawdza przy dużych programach.
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Wstęp do algorytmiki (3) Rozwiązywanie problemów – jak powinno być? Przemyślana implementacja – najpierw się zastanowić i zaplanować, co i jak należy zrobić. Zainwestowanie czasu w „myślenie”, oszczędność czasu w trakcie pisania kodu i testowania.
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Wstęp do algorytmiki (4) Pisanie programu: Podział programu na funkcje (nie umieszczać wszystkiego w main) Sensowne nazwy zmiennych i funkcji (i czy j są krótkie, ale „licznik” czy „suma” są proste w interpretacji, mniejsza szansa błędu) Przejrzystość (czytelność) kodu (switch zamiast długich serii if – else, wcięcia, komentarze, puste linie,…)
Wybrane aspekty programowania w C++ (i nie tylko) PSOiP Wstęp do algorytmiki (4) Pisanie programu: Czytać komunikaty kompilatora, eliminować błędy i ostrzeżenia. Testowanie, sprawdzanie, co się dzieje np. dla bardzo dużych argumentów, dla argumentów na granicy przedziału itd. Zdrowy rozsądek