53 54 55

background image

53. Arytmetyka stałopozycyjna i zmiennopozycyjna.

Zapis stałoprzecinkowy albo stałopozycyjny – jeden ze sposobów zapisu liczb ułamkowych
stosowanych w informatyce. Do zapisu liczby stałoprzecinkowej przeznaczona jest z góry
określona ilość cyfr dwójkowych, a pozycję przecinka ustala się arbitralnie, w zależności od
wymaganej dokładności.

Podziału na część całkowitą i ułamkową dokonuje projektant systemu lub programista, który
przewiduje z jak dużymi liczbami całkowitymi lub z jak dużą dokładnością obliczenia będą
wykonywane. Zwiększanie precyzji liczby to zmniejszanie zakresu, gdyż bity które mają
reprezentować część ułamkową (stać za przecinkiem) nie mogą już reprezentować wartości
całkowitych. Stwierdzenie odwrotne również jest prawdziwe: zwiększanie zakresu
(całkowitoliczbowego) to zmniejszanie precyzji (mniej bitów do dyspozycji na opisanie
części ułamkowej).

W skrajnych wypadkach możliwa jest sytuacja kiedy przecinek będzie stał poza znaczącymi
cyframi liczby. Jeśli będzie on z lewej strony, będziemy mieć zakres mniejszy od 1, jeśli z
prawej – precyzja będzie większa lub równa 1 (równość wystąpi, kiedy przecinek będzie stał
bezpośrednio za cyframi znaczącymi, będziemy mieć wtedy zwykłe liczby całkowite).

Zakresy liczb

Wartość liczby stałoprzecinkowej jest określana tak jak w pozycyjnym systemie dwójkowym.
Wagi bitów części całkowitej mają wartości (kolejno, od najbardziej znaczącego bitu):

, natomiast wagi bitów części ułamkowej mają wartości:

.

Dokładność reprezentacji wynosi 2

n

, czyli jest równa wadze najmniej znaczącego bitu

części ułamkowej.

Na przykład jeśli na część całkowitą zostaną przeznaczone 4 bity (k = 4), natomiast na część
ułamkową 2 bity (n = 2), wówczas:

wartość maksymalna:

1111,11

2

= 2

3

+ 2

2

+ 2

1

+ 2

0

+ 2

-1

+ 2

-2

= 15,75

10

wartość minimalna:

0000,01

2

= 2

-2

= 0,25

10

przykładowa liczba:

1011,10

2

= 2

3

+ 2

1

+ 2

0

+ 2

-1

= 11,5

10

Zastosowania

Zapis stałoprzecinkowy ma tę zaletę, że arytmetyka stałoprzecinkowa może zostać
zrealizowana za pomocą działań całkowitoliczbowych. Dzięki temu działania na ułamkach są
do realizowania tam, gdzie nie ma możliwości użycia liczb zmiennoprzecinkowych: na
procesorach bez jednostki zmienoprzecinkowej, na prostych mikrokomputerach lub w

background image

programach używających rozkazów MMX. Zapis stałoprzecinkowy był także powszechnie
stosowany gdy jednostka zmiennoprzecinkowa procesora była nie dość wydajna, a
jednocześnie nie była potrzebna wysoka dokładność obliczeń, np. w szybkich procedurach
graficznych.

Operacje na liczbach stałoprzecinkowych

Jeśli obliczyć wartość liczby stałoprzecinkowej x w naturalnym kodzie dwójkowym, wartość
ta wyniesie x2

n

. Wówczas działania całkowitoliczbowe mają postać:

Dodawanie/odejmowanie:

- wynik nie wymaga korekty,

jest to zapis stałoprzecinkowy z założoną dokładnością.

Mnożenie: a2

n

b2

n

= ab2

2n

- wynik wymaga korekty, należy podzielić go przez 2

n

, aby

uzyskać postać x2

n

.

Dzielenie całkowitoliczbowe. W tym przypadku dzielną a należy przemnożyć przez
czynnik 2

n

przed wykonaniem dzielenia i wówczas: a2

2n

/ b2

n

= (a / b)2

n

.

Mnożenie i dzielenie przez potęgę dwójki, w tym przypadku 2

n

, jest równoważne

przesunięciu bitowemu (odpowiednio) w lewo bądź prawo o n bitów; jest to operacja bardzo
szybka.

Zapis zmiennoprzecinkowy

Liczba zmiennoprzecinkowa – komputerowa reprezentacja liczb rzeczywistych zapisanych
w postaci wykładniczej (zwanej też notacją naukową). Ze względu na wygodę operowania na
takich liczbach przyjmuje się ograniczony zakres na mantysę i cechę. Powoduje to, że
reprezentacja jest tylko przybliżona a jedna liczba zmiennoprzecinkowa może reprezentować
różne liczby rzeczywiste z pewnego odcinka.

Wartość liczby zmiennoprzecinkowej jest obliczana wg wzoru:

gdzie:

S (sign) – znak liczby, 1 lub -1

M (mantissa) – znormalizowana mantysa, liczba ułamkowa

B (base) – podstawa systemu liczbowego (2 dla systemów komputerowych)

E (exponent) – wykładnik, liczba całkowita

Mantysa jest znormalizowana, tj. należy do przedziału [1,B) (przedział prawostronnie
otwarty!). Jeżeli M jest stałe, a E zmienia się, wówczas przesunięciu ulega przecinek – stąd
właśnie pochodzi nazwa tej reprezentacji.

Zarówno dla mantysy jak i wykładnika ilość cyfr jest z góry ustalona. Zatem dana liczba jest
reprezentowana z pewną skończoną dokładnością i należy do skończonego zbioru wartości.

background image

Załóżmy, że m to liczba cyfr przeznaczonych na mantysę, natomiast n+1 to liczba cyfr
przeznaczonych na wykładnik (n cyfr dla wartości i 1 dla znaku wykładnika). Uznaje się
również, że jedna dodatkowa pozycja (najstarsza) zarezerwowana jest dla zapisu znaku całej
liczby. Wówczas wartości maksymalne i minimalne dla M i E określone są następująco:

Wykładnik E:

o

E

min

= − B

n

+ 1

o

E

max

= B

n

− 1

Mantysa M:

o

M

min

= 1

o

M

max

= BB

− (m − 1)

Stąd najmniejsza i największa liczba dodatnia możliwa do zapisania w takiej reprezentacji to:

.

Zakres liczb, które mogą być reprezentowane w danym zapisie wynosi:

Zero jest wartością specjalną, która nie może zostać bezpośrednio reprezentowana w tym
formacie.

Błąd względny reprezentacji wynosi

(inaczej: waga najmniej znaczącej cyfry

mantysy). Błędów bezwzględnych na ogół się nie podaje.

Jeżeli komputer wygeneruje liczbę

, to traktowana jest jako niedomiar

(underflow).

W przypadku, gdy otrzymana liczba

, to traktowana jest jako

nadmiar wykładniczy (overflow).

Przykład reprezentacji

Przyjmijmy, że B = 10, liczba cyfr dziesiętnych przeznaczonych na mantysę wynosi 4,
natomiast na wykładnik 2. Chcemy zapisać wartość 60,89523.

1. Pierwszy etap to normalizacja mantysy, sytuacja przedstawia się następująco:

. Mantysa M nie należy do zadanego przedziału [1,10),

zatem należy przesuwać przecinek w lewo do chwili, aż wartość M będzie doń
należała. Przesuwanie przecinka w lewo wiąże się ze zwiększaniem E.

2. Po normalizacji (przesunięciu przecinka o 1 pozycje w lewo) otrzymujemy:

background image

3. Ostatnim krokiem jest odpowiednie obcięcie (ang. truncate), albo zaokrąglenie (ang.

round) mantysy do zadanej ilości cyfr.

o

obcięcie:

o

zaokrąglenie:

Przykład dla liczby mniejszej od 1: 0,0000125.

1. Mantysa 0,0000125 nie jest znormalizowana, należy tym razem przesuwać przecinek

w prawo, co wiąże się ze zmniejszaniem E.

2. Po normalizacji, tj. przesunięciu przecinka o 5 pozycje w prawo, otrzymujemy:

.

3. Liczba cyfr znaczących jest mniejsza od dostępnej, więc nie potrzebna żadna forma

zaokrąglania. Liczba ma postać

.

Operacje na liczbach zmiennoprzecinkowych

Własności arytmetyki zmiennoprzecinkowej

Arytmetyka zmiennoprzecinkowa nie jest łączna. To znaczy: iż dla x, y i z mogą zachodzić
różności:


,

ani rozdzielna czyli może zachodzić różność:

Innymi słowy kolejność wykonywania operacji wpływa na końcowy wynik.

Przy obliczeniach zmiennoprzecinkowych występują też inne problemy:

zaokrąglenia

nieprawidłowe operacje

przepełnienie

niedomiar

Dodawanie i odejmowanie

Załóżmy że chcemy dodać lub odjąć dwie dodatnie liczby zmiennoprzecinkowe:

oraz

, przy czym

. Założenia te można spełnić

dla dowolnych liczb zmiennoprzecinkowych manipulując ich kolejnością, znakiem wyniku
oraz rodzajem wykonywanej operacji, wg poniższego schematu:

background image

Wówczas:

Jeśli liczby mają różne wykładniki, to podczas dodawania mantysa liczby o mniejszym
wykładniku musi zostać zdenormalizowana – we wzorze jest to przemnożenie M

2

przez

czynnik

. W szczególnym przypadku, jeśli E

1

E

2

jest większe niż m (ilość cyfr

mantysy), to po tej denormalizacji mantysa będzie miała wartość 0 i liczba o mniejszym
wykładniku nie wpłynie na wynik dodawania bądź odejmowania.

Odejmowanie liczb zmiennoprzecinkowych o takim samym wykładniku E i niewiele
różniącej się mantysie, powoduje że wynikowa mantysa jest znacznie zdenormalizowana.
Renormalizacja w takim wypadku wiąże się z wprowadzeniem sporej ilości nieznaczących
zer na końcu mantysy. Jest to szczególnie niekorzystne, dlatego zwykle tak projektuje się
obliczenia aby do tego nie dopuścić.

Mnożenie i dzielenie

Mając dane liczby zmiennoprzecinkowe

i

:

54: Iteracja i rekurencja

Iteracja – czynność powtarzania (najczęściej wielokrotnego) tej samej instrukcji (albo wielu
instrukcji) w pętli. Mianem iteracji określa się także operacje wykonywane wewnątrz takiej
pętli.

Czynność iteracji przedstawia pętla, zapisana w poniższym pseudokodzie:

i=0
dopóki i<10 wykonuj

i=i+1
przeskocz do góry

W pierwszej iteracji otrzymamy wartość i=1, w następnej i=2, potem i=3 i tak dalej, a
iteracją jest powtarzana tu czynność, czyli zwiększanie zmiennej i o jeden (inkrementacja).

Kod w C/C++:

int i=0;

while (i<10) {
i++;

}

Kod w Pythonie:

background image

i = 0
while i<10:

i += 1

Kod w Javie, za pomocą pętli for:

for(int i=0; i<10; i++){}

Kod w Scheme, funkcyjnym języku programowania:

;; sum : number -> number
;; to sum the first n natural numbers

(define (sum n)
(if (and (integer? n) (> n 0))

(let iter ([n n] [i 1])
(if (= n 1)

i
(iter (- n 1) (+ n i))))

((assertion-violation
'sum "invalid argument" n))))

Rekurencja albo rekursja (recursion) to w logice, programowaniu i w matematyce
odwoływanie się np. funkcji lub definicji do samej siebie. Przykład zastosowania rekurencji w
matematyce: definicja ciągu Fibonacciego

, dla

Jest ona rekurencyjna, gdyż definiuje funkcję odwołując się w definicji do niej samej.

Każda definicja rekurencyjna potrzebuje przynajmniej jednego przypadku bazowego (nie
rekurencyjnego), w tym przypadku są to wartości dla 0 i 1. W przeciwnym wypadku nigdy się
nie zakończy.

Dla przykładu, obliczenie

wygląda następująco:

Rekurencja w programowaniu:

Rekurencja jest podstawową techniką wykorzystywaną w funkcyjnych językach
programowania. Należy jednak zachować ostrożność przy używaniu rekurencji w

background image

rzeczywistych programach. Ryzyko istnieje szczególnie przy przetwarzaniu dużej ilości
głęboko zagnieżdżonych danych.

Jeśli program nie jest w rzeczywistości rekurencyjny, to rekurencja może dramatycznie
zwiększyć złożoność obliczeniową. Ponadto rekurencja zawsze zwiększa pamięciowe
zapotrzebowanie programu (chyba że zostanie użyta możliwa w pewnych przypadkach
optymalizacja zwana rekursją ogonową), gdyż wymaga ona zapamiętania m.in. adresów
powrotu, pozwalających programowi "zorientować się" do którego miejsca ma wrócić po
zakończeniu jednego z wywołań rekurencyjnych. Inną częstą wadą rekurencji jest kompletnie
niezależne rozwiązywanie podproblemów, tak, że czasem jeden problem jest rozwiązywany
w kilku miejscach rozwinięcia rekurencji, np. w powyższym przykładzie obliczania
niepotrzebnie jest dwukrotnie obliczana wartość

(porównaj: programowanie

dynamiczne). Takie problemy nie pojawiają się przy drugim z przykładów. Niezaprzeczalną
zaletą rekurencji jest przejrzystość programów, które z niej korzystają.

Jedną z typowych sytuacji, w których stosuje się rekurencję jest przeszukiwanie danych o
strukturze nieregularnego drzewa, np. XML. Funkcja, która sprawdza czy w danym obiekcie
XML istnieje element o określonej zawartości mogłaby wyglądać następująco (tutaj w PHP
przy użyciu klasy SimpleXML):

function find_text($text, $tree) {


// sprawdź zawartość aktualnego elementu
if ($text == (string)$tree) {
return true;
}

// sprawdź wszystkie jego dzieci
foreach ($tree as $node) {

// tutaj następuje wywołanie rekurencyjne
if (find_text($text, $node)) {
return true;
}

}

// nic nie znaleziono
return false;}

Inny przykład – funkcja obliczająca silnię:

unsigned int silnia(unsigned int n)

{

background image

if (n <= 1) {
return 1;

} else {
return n * silnia(n-1);

}
}

55: Instrukcje warunkowe i pętle

Instrukcja warunkowa jest w inżynierii oprogramowania jednym ze sposobów na
organizację kodu i kontrolowania programu. Procesor sprawdza czy warunek znajdująca się
wewnątrz if jest spełniony (ma wartość logiczną 1). Jeżeli się zgadza to program wykonuje
blok kodu zawarty po instrukcji if. W przeciwnym wypadku opuszcza go i przechodzi do
wykonywania kodu znajdującego się za nim. Istnieją różne typy instrukcji warunkowych:

If

if (warunek) then
{instrukcja jeśli warunek został spełniony}

If-then-else

if (warunek) then
(instrukcja jeśli warunek został spełniony)

Else
(instrukcja gdy warunek nie został spełniony)

End If

Else-if

if warunek then

{instrukcja jeśli warunek został spełniony}
elsif warunek2 then

{instrukcja jeśli warunek2 został spełniony}
elsif warunek3 then

{instrukcja jeśli warunek został spełniony}
else warunek then

{instrukcja jeśli żaden z powyższych warunków nie został spełniony}
end if;

Instrukcja warunkowa jako operator

(warunek)?(instrukcja jeśli warunek został spełniony):(instrukcja jeśli
warunek nie został spełniony)

Tego typu operatory występują w językach o składni typowej dla języka C.

Instrukcja warunkowa w Haskellu

if' :: Bool -> a -> a -> a

if' True x _ = x
if' False _ y = y

Arytmetyczna instrukcja warunkowa (na przykładzie Fortrana)

background image

IF (e) label1, label2, label3

Powyższy kod można zapisać w C++ w ten sposób:

if (e < 0) goto label1

if (e == 0) goto label2
if (e > 0) goto label3

Instrukcja warunkowa w SmallTalku:

var := warunek
ifTrue: [ 'foo' ]

ifFalse: [ 'bar' ]

Instrukcje Case i Switch

Instrukcje case i switch pozwalają na zapisanie skomplikowanej sekwencji warunków w
przystępny sposób. Można je również zapisać za pomocą kombinacji prostych operatorów if,
then i else.

W Pascalu:

case zmienna of

'a': instrukcje wykonane gdy zmienna = a;
'x': instrukcje wykonane gdy zmienna = x;

'y','z': instrukcje wykonane gdy zmienna = y lub zmienna = x;
else instrukcje do wykonania gdy zmienna nie przyjmuje żadnych z

powyższych wartości;
end;

W C/C++:

switch (zmienna) {
case 'a': {instrukcje wykonane gdy zmienna = a;} break;

case 'x': {instrukcje wykonane gdy zmienna = x;} break;
case 'y':

case 'z': {instrukcje wykonane gdy zmienna = y lub zmienna = x;} break;
default: {instrukcje do wykonania gdy zmienna nie przyjmuje żadnych z

powyższych wartości};
;

}

Pętla to jedna z trzech podstawowych konstrukcji programowania strukturalnego (obok
instrukcji warunkowej i instrukcji wyboru). Umożliwia cykliczne wykonywanie ciągu
instrukcji określoną liczbę razy, do momentu zajścia pewnych warunków, dla każdego
elementu kolekcji lub w nieskończoność.

Pętla for:

for (ilośc wykonań) wykonaj (instrukcje).

Przykłady:

background image

Pascal:

var counter: integer;

begin
for counter:=1 to 10 do

WriteLn('Linia numer ', counter);
end.

C++:

for (int i=0; i<10; ++i)
std::cout << i << "-ta iteracja." << std::endl;

PHP:

for ($i=0 ; $i<10 ; ++$i)
echo $i . "-ta iteracja.<br />";

JavaScript:

var i;

for (i=0 ; i<10 ; ++i)
document.write(i + "-ta iteracja.<br />");

Python:

for i in range(1, 10):
print(i, '. iteracja', sep='')

Pętla while:

while ( warunek ) wykonuj (instrukcje)

C++:

unsigned int counter = 5;
unsigned long factorial = 1;


while (counter > 0)

{
factorial *= counter--; /* Multiply and decrement */

}

printf("%lu", factorial);

Java:

int counter = 5;

long factorial = 1;

while (counter > 1)
{

factorial *= counter--;
}

background image

JavaScript:

var counter = 5;
var factorial = 1;


while ( --counter > 1 )

{
factorial *= counter;

}

document.body.appendChild(document.createTextNode(factorial));

LUA:

counter = 5

factorial = 1

while counter > 0 do
factorial = factorial * counter

counter = counter - 1
end


print(factorial)

Pascal:

program Factorial1;
var

Counter, Factorial: integer;
begin

Counter := 5;
Factorial := 1;

while Counter > 0 do
begin

Factorial := Factorial * Counter;
Counter := Counter - 1

end;
WriteLn(Factorial)

end.

Perl:

$factorial *= $counter-- while $counter > 0;

Pętla do-while:

do (instrukcje) while (warunek)

Tego typu pętla działa bardzo podobnie do klasycznych pętli while z tą różnicą, że warunek
jest sprawdzany już po wykonaniu instrukcji, toteż mamy gwarancję że zostaną one
wykonane co najmniej raz.

Java:

unsigned int counter = 5;
unsigned long factorial = 1;

background image

do {
factorial *= counter--; /* Multiply, then decrement. */

} while (counter > 0);
printf("%lu\n", factorial);

C/C++:

unsigned int counter = 5;
unsigned long factorial = 1;

do {
factorial *= counter--; /* Multiply, then decrement. */

} while (counter > 0);
printf("%lu\n", factorial);

JavaScript:

var counter = 5;
var factorial = 1;

do {
factorial *= counter--; /* Multiply, then decrement. */

} while (counter > 0);
document.body.appendChild(document.createTextNode(factorial));

Pascal:

program Factorial;
var

Counter, Factorial: integer;
begin

Counter := 5;
Factorial := 1;


repeat

Factorial := Factorial * Counter;
Dec(Counter);

until Counter = 0;

WriteLn(Factorial);
end.

PHP:

<?php
$counter = 5;

$factorial = 1;
do {

$factorial *= $counter--;
} while ($counter > 0);

echo $factorial;
?>

Foreach

foreach (obiekt): (wykonaj instrukcje)

background image

Pętle foreach wykonują instrukcję dla każdego obiektu z tablicy która została przekazana jako
argument.

Przykłady:

C/C++:

Te języki nie posiadają odpowiednika pętli foreach.

C#:

foreach (type item in set)
{

// do something to item
}

Delphi:

for enumerator in collection do

begin
//do something here

end;

Java:

for (type item: set) {

// do something to item
}

JavaScript:

for (var strProperty in objObject) {
if(objObject.hasOwnProperty(strProperty )) {

/*
do something to:

objObject [strProperty]
*/

}
}

PHP:

foreach ($set as $key => $value) {
echo "{$key} has a value of {$value}";

}


Document Outline


Wyszukiwarka

Podobne podstrony:
48 49 50 51 52 53 54 55 56 57
53 54
54 55 307 POL ED02 2001
54 55
54 55
53,54
rys 53 54
Zes12.53-54
08 1995 54 55
53 54
54 55 56 folia PL
54 55
54 i 55, Uczelnia, Administracja publiczna, Jan Boć 'Administracja publiczna'
53 54
54 55
54 55
53-54-56-69-75, Studia - informatyka, materialy, KSA
54, 55

więcej podobnych podstron