Wstęp do programowania

Podobne dokumenty
Liczby rzeczywiste są reprezentowane w komputerze przez liczby zmiennopozycyjne. Liczbę k można przedstawid w postaci:

Metody numeryczne Technika obliczeniowa i symulacyjna Sem. 2, EiT, 2014/2015

Wstęp do programowania. Reprezentacje liczb. Liczby naturalne, całkowite i rzeczywiste w układzie binarnym

Liczby zmiennoprzecinkowe i błędy

Kod IEEE754. IEEE754 (1985) - norma dotycząca zapisu binarnego liczb zmiennopozycyjnych (pojedynczej precyzji) Liczbę binarną o postaci

Podstawy Informatyki

Systemy liczbowe. 1. Przedstawić w postaci sumy wag poszczególnych cyfr liczbę rzeczywistą R = (10).

Podstawy Informatyki. Inżynieria Ciepła, I rok. Wykład 5 Liczby w komputerze

Wielkości liczbowe. Wykład z Podstaw Informatyki dla I roku BO. Piotr Mika

Metody numeryczne. Janusz Szwabiński. nm_slides.tex Metody numeryczne Janusz Szwabiński 2/10/ :02 p.

Metody numeryczne I. Janusz Szwabiński. Metody numeryczne I (C) 2004 Janusz Szwabiński p.1/61

Wielkości liczbowe. Wykład z Podstaw Informatyki. Piotr Mika

Programowanie w C++ Wykład 2. Katarzyna Grzelak. 4 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 44

Programowanie w C++ Wykład 2. Katarzyna Grzelak. 5 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 41

Met Me ody numer yczne Wykład ykład Dr inż. Mic hał ha Łan Łan zon Instyt Ins ut Elektr Elektr echn iki echn i Elektrot Elektr echn olo echn

Zwykle liczby rzeczywiste przedstawia się w notacji naukowej :

Przedmiot: Urządzenia techniki komputerowej Nauczyciel: Mirosław Ruciński

Zestaw 3. - Zapis liczb binarnych ze znakiem 1

Podstawy Informatyki. Metalurgia, I rok. Wykład 3 Liczby w komputerze

Pracownia Komputerowa wykład VI

Zapis liczb binarnych ze znakiem

Reprezentacja stałoprzecinkowa. Reprezentacja zmiennoprzecinkowa zapis zmiennoprzecinkowy liczby rzeczywistej

Dane, informacja, programy. Kodowanie danych, kompresja stratna i bezstratna

Architektura komputerów Reprezentacja liczb. Kodowanie rozkazów.

Python wstęp do programowania dla użytkowników WCSS

Wprowadzenie do architektury komputerów systemy liczbowe, operacje arytmetyczne i logiczne

Teoretyczne Podstawy Informatyki

ARYTMETYKA BINARNA. Dziesiątkowy system pozycyjny nie jest jedynym sposobem kodowania liczb z jakim mamy na co dzień do czynienia.

Pułapki liczb zmiennoprzecinkowych. Adam Sawicki asawicki.info

Kod U2 Opracował: Andrzej Nowak

Wprowadzenie do metod numerycznych. Krzysztof Patan

Architektura komputerów

Systemy zapisu liczb.

Pracownia Komputerowa wyk ad VI

LICZBY ZMIENNOPRZECINKOWE

SYSTEMY LICZBOWE. SYSTEMY POZYCYJNE: dziesiętny (arabski): 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 rzymski: I, II, III, V, C, M

ARCHITEKTURA KOMPUTERÓW Liczby zmiennoprzecinkowe

Technologie Informacyjne

Typ użyty w deklaracji zmiennej decyduje o rodzaju informacji, a nazwa zmiennej symbolicznie opisuje wartość.

Dokładność obliczeń numerycznych

Podstawowe operacje arytmetyczne i logiczne dla liczb binarnych

BŁĘDY OBLICZEŃ NUMERYCZNYCH

Wstęp do informatyki- wykład 4 Deklaracja zmiennych Typy

Reprezentacja symboli w komputerze. Liczby całkowite i zmiennoprzecinkowe. Programowanie Proceduralne 1

Adam Korzeniewski p Katedra Systemów Multimedialnych

2018 Marcin Kukliński. Niesforne bity i bajty

Podstawy Programowania Podstawowa składnia języka C++

SYSTEMY LICZBOWE. Zapis w systemie dziesiętnym

ARCHITEKRURA KOMPUTERÓW Kodowanie liczb ze znakiem

Pracownia komputerowa. Dariusz Wardecki, wyk. VI

Pracownia Komputerowa wykład V

Arytmetyka binarna - wykład 6

Adam Korzeniewski p Katedra Systemów Multimedialnych

Samodzielnie wykonaj następujące operacje: 13 / 2 = 30 / 5 = 73 / 15 = 15 / 23 = 13 % 2 = 30 % 5 = 73 % 15 = 15 % 23 =

Obliczenia Naukowe. O arytmetyce komputerów, Czyli jak nie dać się zaskoczyć. Bartek Wilczyński 29.

Architektura komputerów

Dr inż. Grażyna KRUPIŃSKA. D-10 pokój 227 WYKŁAD 2 WSTĘP DO INFORMATYKI

C++ wprowadzanie zmiennych

Podstawy Informatyki

SYSTEMY LICZBOWE 275,538 =

Arytmetyka stało i zmiennoprzecinkowa

Technologie Informacyjne Wykład 4

Wstęp do informatyki- wykład 2

Wstęp do informatyki- wykład 1 Systemy liczbowe

Kodowanie liczb całkowitych w systemach komputerowych

Pracownia Komputerowa wykład IV

2 Przygotował: mgr inż. Maciej Lasota

Programowanie C++ Wykład 2 - podstawy języka C++ dr inż. Jakub Możaryn. Warszawa, Instytut Automatyki i Robotyki

Arytmetyka liczb binarnych

LABORATORIUM PROCESORY SYGNAŁOWE W AUTOMATYCE PRZEMYSŁOWEJ. Zasady arytmetyki stałoprzecinkowej oraz operacji arytmetycznych w formatach Q

1.1. Pozycyjne systemy liczbowe

Podstawy Informatyki. Inżynieria Ciepła, I rok. Wykład 10 Kurs C++

Programowanie. programowania. Klasa 3 Lekcja 9 PASCAL & C++

METODY NUMERYCZNE. Po co wprowadzamy liczby w formacie zmiennoprzecinkowym (floating point)?

DYDAKTYKA ZAGADNIENIA CYFROWE ZAGADNIENIA CYFROWE

Arytmetyka komputera. Na podstawie podręcznika Urządzenia techniki komputerowej Tomasza Marciniuka. Opracował: Kamil Kowalski klasa III TI

Dane, informacja, programy. Kodowanie danych, kompresja stratna i bezstratna

Instrukcja do ćwiczeń nr 4 typy i rodzaje zmiennych w języku C dla AVR, oraz ich deklarowanie, oraz podstawowe operatory

Metody numeryczne II. Reprezentacja liczb

1 P roste e t ypy p d a d n a ych c - c ąg ą g d a d l a szy 2 T y T py p z ł z o ł żo ż ne e d a d n a ych c : T BLICE

Dodatek do Wykładu 01: Kodowanie liczb w komputerze

Podstawą w systemie dwójkowym jest liczba 2 a w systemie dziesiętnym liczba 10.

Pozycyjny system liczbowy

Naturalny kod binarny (NKB)

ALGORYTMY I STRUKTURY DANYCH

Arytmetyka liczb wymiernych w języku C++

5. Rekurencja. Przykłady

REPREZENTACJA LICZBY, BŁĘDY, ALGORYTMY W OBLICZENIACH

W jaki sposób użyć tych n bitów do reprezentacji liczb całkowitych

System liczbowy jest zbiorem reguł określających jednolity sposób zapisu i nazewnictwa liczb.

Wstęp do informatyki- wykład 4 Deklaracja zmiennych Typy Instrukcja selekcji if-else

Wstęp do Informatyki i Programowania (kierunek matematyka stosowana)

Metoda znak-moduł (ZM)

EMN. dr Wojtek Palubicki

Kodowanie informacji. Kody liczbowe

Informatyka 1. Wykład nr 5 ( ) Politechnika Białostocka. - Wydział Elektryczny. dr inŝ. Jarosław Forenc

Wprowadzenie do informatyki - ć wiczenia

Arytmetyka stałopozycyjna

Kod uzupełnień do dwóch jest najczęściej stosowanym systemem zapisu liczb ujemnych wśród systemów binarnych.

Programowanie strukturalne i obiektowe. Funkcje

Transkrypt:

Wstęp do programowania Wykład 4 Reprezentacja liczb Janusz Szwabiński Plan wykładu: Zamiast motywacji Reprezentacja liczb całkowitych Reprezentacja liczb rzeczywistych Dokładność w obliczeniach komputerowych Materiały uzupełniające: D. Goldberg, "What Every Computer Scientist Should Know About Floating Point Arithmetic", Computing Surveys (1991) Zamiast motywacji Z lekcji matematyki wiemy, że cos = 0 π 2. Zobaczmy, jak to wygląda w Pythonie: In [7]: import math In [8]: math.cos(math.pi/2) Out[8]: 6.123233995736766e-17 Wynik jest wprawdzie bardzo mały, jednak różni się od zera. Inny przykład: In [9]: x = 2**30

In [10]: x + 2**(-22)==x Out[10]: False In [11]: x + 2**(-23)==x Out[11]: True Okazuje się, że w drugim przypadku komputer traktuje obie liczby jako identyczne! I jeszcze jeden przykład: In [12]: y = 1000.2 In [13]: x = y - 1000.0 In [14]: print(x) 0.20000000000004547 0.2 Wynik różni się nieznacznie od wartości dokładnej! Powyższe przykłady to efekty skończonej dokładności maszyny liczącej, jaką jest komputer. Nie zawsze są one szkodliwe dla prowadzonych obliczeń, jednak warto wiedzieć, dlaczego powstają. Liczby całkowite Liczby całkowite są reprezentowane dokładnie w pamięci komputera, jednak jest ich skończona liczba. W większości języków programowania jest to związane ze stałą liczbą bitów używanych do ich przechowywania. n W ogólności, bitowa liczba całkowita ma wartości od 2^(n 1) do 2^(n 1) 1. Na maszynach 32 bitowych daje to zakres: In [15]: n = 32 imin = -2**(n-1) imax = 2**(n-1)-1 print(imin,"< i <",imax) -2147483648 < i < 2147483647

Natomiast dla maszyn 64 bitowych otrzymamy: In [16]: n = 64 imin = -2**(n-1) imax = 2**(n-1)-1 print(imin,"< i <",imax) -9223372036854775808 < i < 9223372036854775807 W Pythonie jest jednak trochę inaczej. W wersji 2.7 rzeczywiście mamy do dyspozycji typ into powyższych zakresach, jednak wyjście poza zakres powoduje automatyczne przełączenie się interpretera na typ long into teoretycznie nieograniczonej precyzji (liczbie bitów). W praktyce precyzja jest ograniczona pamięcią komputera. Natomiast w wersji 3.X nie ma już rozróżnienia na inti long int. Dlatego w Pythonie można liczyć na liczbach dużo większych niż te zdefiniowane powyżej: In [17]: 2*10**100 Out[17]: 2000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000 Ponieważ jednak Python należy pod tym względem raczej do wyjątków wśród języków programowania, omówimy teraz dwie najczęściej stosowane reprezentacje liczb całkowitych, z których ograniczeń użytkownik Pythona może nie zdawać sobie sprawy. Reprezentacja prosta (bez znaku) N bitowa liczba całkowita z = ( a N 1, a N 2,, a0), a i = {0, 1} i = 0,, N 1 bez znaku jest reprezentowana jako: gdzie ( ) to poszczególne bity. Jej wartość wyliczymy w następujący sposób: z = N 1 i=0 a i 2 i. Natomiast zakres tej reprezentacji to: 0 z 2 N 1. z In [18]: n = 64 imax = 2**(n)-1 print("0 < i <",imax) 0 < i < 18446744073709551615

Przykłady liczb w 8 bitowej reprezentacji: Liczba Reprezentacja 0 00000000 2 00000010 51 00110011 127 01111111 255 11111111

Reprezentacja uzupełnieniowa (ze znakiem) N bitowa liczba całkowita z = (,,, ), = {0, 1}, a N 1 a N 2 a0 a i jednak teraz wyliczamy jej wartość ze wzoru: z = a N 1 2 N 1 + N 2 i=0 a i 2 i. Zakres tej reprezentacji: 2 N 1 z 2 N 1 1 z ze znakiem na pierwszy rzut oka reprezentowana jest tak samo, tzn.: Jedną z zalet tej reprezentacji jest łatwe generowanie liczb ujemnych: Ponadto nie trzeba rozróżniać liczb na dodatnie i ujemne przy dodawaniu: Poniżej znajdują się przykłady liczb w 8 bitowej reprezentacji uzupełnieniowej: Liczba Reprezentacja 127 01111111 51 00110011 2 00000010 2 11111110 51 11001101 128 10000000

Warto również wspomnieć, że mnożenie przez 2 w tej reprezentacji odpowiada przesunięciu bitów o jedną pozycję w lewo, a dzielenie przez 2 o jedną pozycję w prawo. In [19]: x = 4 In [20]: x << 1 Out[20]: 8 In [21]: x >> 1 Out[21]: 2 Jedyny problem w obliczeniach komputerowych na liczbach całkowitych to wyjście poza zakres (podkreślam jeszcze raz nie dotyczy to Pythona!). Rozważmy następujący przykład w napisany w języku C++:

In [22]: %%writefile silnia.cpp #include <iostream> int silnia(int n) { if (n<=1) return 1; else return n*silnia(n-1); } int main () { int n; std :: cout << "Podaj liczbę: "; std :: cin >> n; std :: cout << n << "!= " << silnia(n) << "\n"; } Overwriting silnia.cpp In [23]:!g++ silnia.cpp Efekt kilku wywołań tego programu jest następujący: Podaj liczbę: 12 12!=479001600 Podaj liczbę: 16 16!=2004189184 Podaj liczbę: 17 17!= 288522240 Mimo,że przy obliczaniu silni mnożymy ze sobą liczby dodatnie, ostatni wynik jest ujemny. Jest to związane z tym, że najczęściej (i C++nie jest tu wyjątkiem) typy całkowite mają strukturę pierścienia, w której zwiększenie liczby największej o 1 da w wyniku liczbę najmniejszą. I odwrotnie zmniejszenie liczby najmniejszej o 1 da liczbę największą.

Jeżeli nie pamiętamy, jakie zakresy mają poszczególne typy danych, najczęściej możliwe jest ich podględnięcie z poziomu akurat używanego języka. Np. w C++odpowiednie stałe zdefiniowane są w pliku nagłówkowym climits: In [24]: %%writefile cints.cpp #include <iostream> #include <climits> int main () { std :: cout << SHRT_MAX << "\n" ; std :: cout << INT_MAX << "\n" ; std :: cout << LONG_MAX << "\n" ; } Overwriting cints.cpp In [25]:!g++ cints.cpp -o cints In [26]:!./cints 32767 2147483647 9223372036854775807 W Fortranie do wyświetlenia największej liczby w danym typie służy funkcja huge: In [27]: %%writefile fints.f90 program integers implicit none integer(kind=1) :: a integer(kind=2) :: b integer(kind=4) :: c integer(kind=8) :: d integer(kind=16) :: e write(6,*) huge(a) write(6,*) huge(b) write(6,*) huge(c) write(6,*) huge(d) write(6,*) huge(e) end program integers Overwriting fints.f90 In [28]:!gfortran fints.f90 -o fints

In [29]:!./fints 127 32767 2147483647 9223372036854775807 170141183460469231731687303715884105727 Typ intpythona radzi sobie oczywiście dużo lepiej przy wiliczaniu silni: In [30]: def silnia(n): if n<=1: return 1 else: return n*silnia(n-1) In [31]: silnia(17) Out[31]: 355687428096000 In [32]: silnia(100) Out[32]: 9332621544394415268169923885626670049071596826438162146859296389521 7599993229915608941463976156518286253697920827223758251185210916864 000000000000000000000000 Należy mieć przy tym świadomość, że "dowolną" precyzję otrzymujemy kosztem wydajności obliczeń. Np. jeżeli wszystkie liczby występujące w obliczeniach i wyniki działań na nich mieszczą się w zakresie odpowiadającym podstawowemu typowi intw Pythonie 2.7, wówczas obliczenia w Pythonie 2.7 zostaną wykonane szybciej niż w 3.5.

Liczby zmiennopozycyjne Reprezentacja zmiennopozycyjna to najczęściej obecnie stosowany sposób zapisu liczb rzeczywistych na komputerach. W reprezentacji tej liczba przedstawiona jest za pomocą bazy oraz precyzji (liczby cyfr). Przykład: jeśli i, to liczba będzie miała reprezentację. Problemy: B = 10 p = 3 0.1 1.00 10 1 ze względu na skończoną dokładność (liczbę bitów) nie wszystkie liczby dadzą się przedstawić dokładnie np. dla i liczba będzie miała tylko reprezentację przybliżoną B = 2 p = 24 0.1 1.10011001100110011001100 2 4 = 0.0688 B p Postać ogólna Liczba zmiennopozycyjna w bazie i precyzji będzie miała postać: B e Zwróćmy uwagę, że reprezentacja ta nie jest jednoznaczna: liczby przedstawiają liczbę w bazie i precyzji. B p ±d. dd d, ±( d0 + d1b 1 + + d p 1B (p 1) ) B e, 0 d i B 0.1 B = 10 p = 3 0.01 10 1 i 1.00 10 1 Reprezentacja znormalizowana Aby uczynić reprezentację jednoznaczną, zakłada się często : uzyskana w ten sposób reprezentacja liczb jest rzeczywiście jednoznaczna, ale... niemożliwe jest przedstawienie w niej zera w naturalny sposób najczęściej przyjmuje się, że 0 reprezentowane jest przez d0 0 1.00 B e 1 min

Standard IEEE 754 IEEE 754 (https://pl.wikipedia.org/wiki/ieee_754 (https://pl.wikipedia.org/wiki/ieee_754)) to standard reprezentacji binarnej i operacji na liczbach zmiennoprzecinkowych. Został on ustanowiony w 1985 r przez Institute of Electrical and Electronics Engineers. Ponieważ rozwiązywał wiele problemów obecnych we wcześniejszych implementacjach reprezentacji zmiennopozycyjnych, stosowany jest powszechnie we współczesnych procesorach i oprogramowaniu obliczeniowym. B = 2 p = 24 i dla liczb w pojedynczej precyzji B = 2 i p = 53 dla liczb w podwójnej precyzji w pojedynczej precyzji liczba maszynowa ma postać: g g = ( 1) s 1.m 2 e 127 przesunięcie wykładnika o 127 (w podwójnej precyzji o 1023) powoduje, że do jego przedstawienia wystarczą liczby dodatnie w pojedynczej precyzji można przedstawić liczby rzeczywiste z zakresu w podwójnej precyzji z zakresu wartości specjalne: wartość NaN + ( ) 0 0/0, /, Liczba Reprezentacja to wynik działań: x mod 0 mod y x, x < 0 3.4 10 38 q 3.4 10 38 1.8 10 308 q 1.8 10 308 0 0 00000000 00000000000000000000000 0 1 00000000 00000000000000000000000 0 11111111 00000000000000000000000 1 11111111 00000000000000000000000 N an 0 11111111 00000100000000000000000 N an 1 11111111 00100010001001010101010 najmniejsza co do modułu liczba różna od zera w reprezentacji znormalizowanej ±1, 1754 10 38 ±2, 225 10 308 w pojedynczej precyzji w podwójnej precyzji liczby mniejsze od wartości granicznej w każdej precyzji traktowane jako 0

w celu zwiększenia dokładności obliczeń standard IEEE wprowadza również wartości zdenormalizowane wszystkie bity cechy równe 0 i przynajmniej jeden bit mantysy różny od zera najmniejsza liczba w pojedynczej precyzji to najmniejsza liczba w podwójnej precyzji to ±1, 40 10 45 ±4, 94 10 324 In [33]: import sys In [34]: sys.float_info Out[34]: sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_ex p=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, round s=1) In [35]: fmax = sys.float_info.max In [36]: print(fmax) 1.7976931348623157e+308 In [37]: 2*fmax Out[37]: inf In [38]: -2*fmax Out[38]: -inf In [39]: 1/(2*fmax) Out[39]: 0.0

Przykład reprezentacja liczby 5,375 Przekształcamy liczbę dziesiętną do postaci dwójkowej: 5, 375 = 1 2 2 + 0 2 1 + 1 2 0 + 0 2 1 + 1 2 2 + 1 2 3 101.011 Otrzymaną liczbę normalizujemy: 101.011 2 0 = 1.01011 2 2 Pomijamy wiodącą jedynkę w mantysie: 01011 Obliczamy wykładnik: 2 + 127 = 129 10000001 Po określeniu bitu znaku otrzymujemy ostatecznie: znak wykładnik mantysa 0 10000001 01011000000000000000000 Typowe problemy w obliczeniach zmiennopozycyjnych Z omówioną powyżej reprezentacją wiążą się pewne niedokładności w codziennych obliczeniach. Rozważmy przykład (w C++): In [40]: %%writefile reprezentacja.cpp #include <iostream> int main() { float x=0.01; if(100.0*x==1.0) std::cout << "Równe :)\n"; else std::cout << "Nierówne :(\n"; } Overwriting reprezentacja.cpp In [41]:!g++ reprezentacja.cpp -o rep In [42]:!./rep Nierówne :( Przyczyną takiego działania programu jest fakt, że liczba 0.01 nie ma dokładnej reprezentacji w standardzie IEEE, i jest przybliżana najbliższą liczbą maszynową. Podobnie:

In [43]: %%writefile reprezentacja2.cpp #include <iostream> int main() { float x=77777.0, y=7.0; float y1 = 1.0/y; float z= x/y; float z1 = x*y1; if(z==z1) std::cout << "Równe :)\n"; else std::cout << "Nierówne :(\n"; } Overwriting reprezentacja2.cpp In [44]:!g++ reprezentacja2.cpp -o rep2 In [45]:!./rep2 Nierówne :( 1000, 2 Kolejny przykład również związany jest z niedokładną reprezentacją, tym razem liczby : In [52]: %%writefile reprezentacja3.cpp #include <iostream> int main() { float y=1000.2; float x=y-1000.0; std::cout << x <<"\n"; } Overwriting reprezentacja3.cpp In [53]:!g++ reprezentacja3.cpp -o rep3 In [54]:!./rep3 0.200012 Sprawdźmy, czy podobne efekty można zaobserwować w Pythonie:

In [55]: x = 0.01 In [57]: 100*x == 1.0 Out[57]: True In [58]: x=77777.0; y=7.0 y1 = 1.0/y; z= x/y z1 = x*y1 In [59]: z1 == z Out[59]: True In [60]: y = 1000.2 x = y - 1000.0 In [61]: print(x) 0.20000000000004547 Na pierwszy rzut oka Python wydaje się lepszy, ponieważ tylko w jednym z trzech przykładów obserwujemy problem związany z reprezentacją zmiennopozycyjną. Należy jednak zwrócić uwagę, że Python używa liczb zmiennopozycyjnych o podwójnej precyzji. Natomiast w powyższych przykładach w C++użyty został tym floato pojedynczej precyzji. Przepisanie tych kodów na typ double, równoważny temu w Pythonie, prowadzi do następujących wyników: In [62]: %%writefile reprezentacjab.cpp #include <iostream> int main() { double x=0.01; if(100.0*x==1.0) std::cout << "Równe :)\n"; else std::cout << "Nierówne :(\n"; } Writing reprezentacjab.cpp

In [63]:!g++ reprezentacjab.cpp -o repb In [64]:!./repb Równe :) In [65]: %%writefile reprezentacja2b.cpp #include <iostream> int main() { double x=77777.0, y=7.0; double y1 = 1.0/y; double z= x/y; double z1 = x*y1; if(z==z1) std::cout << "Równe :)\n"; else std::cout << "Nierówne :(\n"; } Writing reprezentacja2b.cpp In [66]:!g++ reprezentacja2b.cpp -o rep2b In [67]:!./rep2b Równe :) In [71]: %%writefile reprezentacja3b.cpp #include <iostream> int main() { double y=1000.2; double x=y-1000.0; std::cout.precision(15); std::cout << x <<"\n"; } Overwriting reprezentacja3b.cpp In [72]:!g++ reprezentacja3b.cpp -o rep3b

In [73]:!./rep3b 0.200000000000045 Jak widać, po przejściu na podwójną precyzję w C++część problemów zniknęła. Otrzymaliśmy wyniki takie same, jak te w przykładach w Pythonie. Oczywiście, również w podwójnej precyzji możemy obserwować podobne błędy: In [76]: x = 0.1 + 0.1 + 0.1 In [77]: x == 0.3 Out[77]: False In [78]: print(x) 0.30000000000000004 Jak widać w powyższych przykładach, bezpośrednie porównywanie liczb zmiennopozycyjnych na komputerze nie zawsze daje oczekiwany wynik. Dlatego w praktyce bardzo często traktować dwie liczby jako równe, jeżeli moduł ich różnicy jest mniejszy od małej, z góry zadanej wartości: In [79]: eps = 1.0e-6 x = 0.1 + 0.1 + 0.1 y = 0.3 In [80]: x == y Out[80]: False In [81]: abs(x-y) < eps Out[81]: True

Uzyskaliśmy pożądany wynik, jednak ta metoda generuje kilka nowych problemów: ϵ określenie odpowiedniego nie zawsze jest sprawą oczywistą relacja zdefiniowana poprzez a b a b < ϵ nie jest relacją równoważności! Jeżeli w programie wykonujemy wiele porównań, wówczas wybieramy wartość ϵ tak, aby określała ona maksymalny błąd względny, jaki gotowi jesteśmy popełnić przy porównaniu, a następnie porównywać moduł różnicy nie z samym, a raczej z iloczynem i modułu jednej z porównywanych liczb: ϵ ϵ In [82]: eps = 1.0e-6 x = 0.1 + 0.1 + 0.1 y = 0.3 In [83]: abs(x-y) < eps*abs(y) Out[83]: True