Dokumentacja projektu
"Metoda Newtona. Metody zmodyfikowane
dla pierwiastków wielokrotnych"
1. Wprowadzenie
Opis użytej metody Newtona i ogólne wprowadzenie do projektu zostało
podane w części opisującej funkcję main();
2. Sposób kompilacji projektu
Kolejne kroki potrzebne do kompilacji projektu:
-
Należy rozpakować plik z projektem.
-
Należy prawdzić, czy opcje w pliku "config.h" odpowiadają potrzebom użytkownika
i ewentualnie je zmienić:
-
jeżeli projekt ma być skompilowany z kodem używanym podczas sprawdzania
poprawności jego działania, należy usunąć komentarz w linii "#define DEBUG"
-
jeżeli zachodzi taka potrzeba, należy zwiększyć bufor przeznaczony na zapamiętywanie
wzorów funkcji (w linii "#define MAX_ELEMENTOW 100" należy zwiększyć liczbę
100)
Następnie skompilować projekt (np. używając kompilatora gcc)
3. Użytkowanie projektu
Program "projekt" po uruchomieniu wymaga czterech linijek danych wprowadzonych
od użytkownika:
-
w pierwszej należy podać wzór funkcji f(x),
-
w drugiej zadaną dokładność (dana liczba a jest pierwiastkiem równania
f(x)=0, jeżeli f(a)=0; w praktyce jednak użytkownikowi może zależeć, aby
f(a) tylko w przybliżeniu było równe 0 - program przyjmuje, iż dana liczba
jest pierwiastkiem, jeżeli f(a) jest mniejsze bądź równe od podanej dokładności),
-
w trzeciej punkt startowy obliczeń (od jakiego x należy zacząć szukanie
pierwiastka)
-
w czwartej ilość kroków, w których program może próbować znaleźć przybliżonego
pierwiastka
Dane te można wpisać w pliku, a następnie przekazać do projektu w następujący
sposób:
./projekt < plik
Przykładowy plik może wyglądać następująco:
x^4-4*x^3-5*x^2-4*x+4+sin(x)+ln(10)
0.0005
3
50
Program ignoruje spacje wprowadzone przez użytkownika.
Znakiem oddzielającym części całkowite od ułamkowych w liczbach jest
kropka.
Przy wprowadzaniu wzoru funkcji zmienną jest "x". Możliwe jest używanie
następujących operatorów matematycznych "+", "-", "*", "/", "^", następujących
stałych matematycznych "pi" i "e" oraz funkcji matematycznych "sin", "cos",
"tan", "ctg", "ln".
Po przyjęciu danych i sprawdzeniu ich poprawności program przystępuje
do obliczeń (wypisując dla kolejnych przybliżeń pierwiastka wartość funkcji
i pochodnej), a następnie pokazuje wynik obliczeń.
4. Znane błędy i ograniczenia projektu
W przypadku, gdyby wynik działania projektu był różny od oczekiwanych,
należy skompilować go z kodem używanym podczas sprawdzania poprawności
jego działania (zostało to opisane w punkcie 2).
W przypadku, gdyby zaszła potrzeba skierowania komunikatów debugujących
do pliku, należy użyć komendy "./projekt 2> plik" (jeżeli zachodzi
konieczność zapisu do pliku normalnych komunikatów programu, używamy komendy
"./projekt
> plik").
Należy pamiętać, iż czasami występujące niezgodności mogą wynikać z
tego, iż projekt przy obliczaniu kolejnych wartości stosuje przybliżoną
dokładność (błędy mogą się sumować).
Od użytkownika nie jest wymagane podawanie przedziału obliczeń (zgodnie
z założeniami metody) Jest to wygodne, gdyż nie musi on znać przebiegu
zmienności funkcji, natomiast wymaga, aby ilość kroków, w których program
ma szukać przybliżonego pierwiastka, była możliwie duża. Inaczej ciąg kolejnych
pierwiastków może nie być wystarczająco "szybko" zbieżny do zera i projekt
wykaże brak pierwiastka. Zostało to wyjanione dokładniej w opisie funkcji
main();
Należy również pamiętać o podawaniu możliwie najmniejszej wartości dokładności,
jeżeli zależy nam na możliwie dokładnym obliczeniu pierwiastka podanej
funkcji.
5. Budowa poszczególnych plików
projektu
Makrodefinicje i stałe
Nazwa |
Opis |
dprintf |
funkcja wypisująca dane wyłącznie, gdy zdefiniowaną stałą DEBUG (w
pliku config.h) |
DEBUG |
Jej zdefiniowanie włącza kompilowanie kodu używanego przy pisaniu programu |
przyrost |
Przyrost, dla którego ma być liczona pochodna funkcji zgodnie ze wzorem:
f'(x)=(f(x+przyrost)-f(x))/przyrost
Im mniejszy, tym dokładniej liczona pochodna |
MAX_ELEMENTOW |
Ilość elementów w każdej strukturze LICZBA |
ST_DODAWANIE |
Używana przy liczeniu wag w strukturze LICZBA. Definiuje dodawanie |
ST_ODEJMOWANIE |
Używana przy liczeniu wag w strukturze LICZBA. Definiuje odejmowanie |
ST_MNOZENIE |
Używana przy liczeniu wag w strukturze LICZBA. Definiuje mnożenie |
ST_DZIELENIE |
Używana przy liczeniu wag w strukturze LICZBA. Definiuje dzielenie |
ST_POTEGOWANIE |
Używana przy liczeniu wag w strukturze LICZBA. Definiuje potęgowanie |
ST_FUNKCJA |
Używana przy liczeniu wag w strukturze LICZBA. Definiuje funkcje
matematyczne |
Struktury danych
LICZBA
Opis: struktura przechowująca opis funkcji (pochodnej) w postaci zrozumiałej
dla programu. Szerzej jej zastosowanie zostało opisane w funkcji wartosc_funkcji;
Nazwa |
Typ |
Opis |
liczby[MAX_ELEMENTOW] |
double |
Przechowuje liczby kolejnych elementów funkcji |
stringi[MAX_ELEMENTOW][100] |
char |
Przechowuje napisy kolejnych elementów funkcji |
wagi[MAX_ELEMENTOW] |
int |
Przechowuje wagi kolejnych elementów funkcji |
ile |
int |
Ilość elementów działania przechowywanych w liczby, stringi i wagi |
Zmienne globalne
Nazwa |
Typ |
Opis |
wart_funkcji |
double |
Wartość funkcji wyliczona dla konkretnego x |
wart_pochodnej |
double |
Wartość pochodnej funkcji wyliczona dla konkretnego x |
wzor[1000] |
char |
Wzór funkcji wczytanej od użytkownika |
mojblad |
int |
Błąd zwracane prze funkcje z funkcje.c |
Funkcje
int main()
Opis: główna funkcja programu. W funkcji tej zawarty jest kod związany
bezpośrednio z implementacją metody Newtona.
Zmienne:
Nazwa |
Typ |
Opis |
j |
int |
Używana w pętli |
i |
int |
Używana przy zwracaniu wartości błędu przez funkcje
licz_funkcja_pochodna
i wczytaj_liczba |
ile_krokow |
int |
W ilu krokach program ma próbować znaleźć pierwiastek |
iks |
double |
Aktualna wartość przybliżenia pierwiastka |
dokladnosc |
double |
Dokladność, z jaką ma być znaleziony pierwiastek |
wynik |
int |
Kod błędu, który jest zarazem kodem błędu programu |
Wprowadzenie do metody Newtona:
Jest to metoda pozwalająca na obliczanie przybliżonych wartości pierwiastków
(zarówno o parzystej i nieparzystej wielokrotności) równania f(x)=0.
Założenia tej metody są następujące: w przedziale <a;b>, w którym
położony jest pierwiastek, funkcja f(x) ma na krańcach różne znaki oraz
pierwsza i druga pochodna mają stałe znaki.
Kolejne przybliżenia pierwiastków są wtedy równe:
xn+1 = xn - f(xn)/f'(xn)
Projekt jest praktyczną realizacją tej metody. Pozwala on na policzenie
pierwiastka z zadaną dokładnością.
Wykorzystuje on jeden z trzech sposobów implementacji podanych w książce
"Metody numeryczne" Zenona Fortuny, Bohdana Macukowa i Janusza Wąsowskiego.
Zgodnie z nią nie jest potrzebne sprawdzanie założeń metody.
Wystarczy liczenie kolejnych (ewentualnych) przybliżeń zgodnie z podanym
wzorem i sprawdzanie dla każdego, czy jest już poszukiwanym pierwiastkiem
(przy czym pod pojęciem pierwiastka równania rozumiemy liczbę, dla której
wartość funkcji f(x) jest mniejsza od podanej przez użytkownika dokładności).
Aby program nie wpadł w pętlę nieskończoną, można podać, dla ilu maksymalnie
przybliżeń program ma sprawdzać, czy są pierwiastkami. Jest to bardzo wygodne,
gdyż użytkownik nie musi znać przebiegu zmienności f(x). Aby mieć jednak
pewność, iż rzeczywiście nie pominęliśmy pierwiastka, należy podawać maksymalnie
dużą ilość kroków.
Zostało to zrealizowane ogólnie w następujący sposób:
-
wczytanie parametrów początkowych
-
jeżeli jest to możliwe, liczenie kolejnego przybliżenia zgodnie w powyższym
wzorem.
-
sprawdzanie, czy osiągnięto pierwiastek
-
powrót do punktu 2, jeżeli policzyliśmy dotąd mniej przybliżeń niż podał
użytkownik
Szczegółowy algorytm:
-
START
-
wczytanie danych do zmiennych wzór, dokladnosc, iks, ile_krokow.
-
wart_funkcji=wartości funkcji dla podanego iks (wykorzystując funkcję
licz_funkcja_pochodna).
Jeżeli wystąpił błąd, wyjście z programu.
-
wart_pochodnej=i wartości pochodnej funkcji dla podanego iks (wykorzystując
funkcję licz_funkcja_pochodna). Jeżeli wystąpił błąd, wyjście z
programu.
-
sprawdzenie, czy osiągnięto pierwiastkek. Jeżeli tak, wypisanie komunikatu
i wyjście z programu.
-
jeżeli wartość pochodnej funkcji (zmienna wart_pochodnej)==0, wypisanie
komunikatu i wyjście z programu
-
obliczenie nowego przybliżenia pierwiastka zgodnie ze wzorem iks = iks
- wart_funkcji/wart_pochodnej (zgodnie ze wzorem z metody Newtona: xn+1
= xn - f(xn)/f'(xn)).
-
wart_funkcji=wartości funkcji dla podanego iks (wykorzystując funkcję
licz_funkcja_pochodna).
Jeżeli wystąpił błąd, wyjście z programu.
-
wart_pochodnej=i wartości pochodnej funkcji dla podanego iks (wykorzystując
funkcję licz_funkcja_pochodna). Jeżeli wystąpił błąd, wyjście z
programu.
-
zwiększenie j
-
powrót do kroku 1, jeżeli j<=ile_kroków.
-
Wypisanie komunikatu o braku pierwiastka
-
STOP
int wczytaj_liczba(double *liczba, int dodatnia)
Opis: funkcja wczytująca liczbę z pliku stdin do zmiennej liczba
Zmienne:
Nazwa |
Typ |
Opis |
ciag[1000] |
char |
Bufor na wczytanie linii |
wartosc |
double |
Wczytana wartość liczby |
i |
int |
Miejsce na kod błędu zwracany z funkcji wez_liczba |
*liczba |
double |
Zmienna pozwalająca zwrócić liczbę na zewnątrz |
dodatnia |
int |
Jeżeli jest równa 1, zwracane są tylko liczby dodatnie |
wynik |
int |
zwracany kod błędu |
Algorytm:
-
START
-
Pobranie linii o maksymalnej długości 1000 znaków z stdin do zmiennej ciag
-
przekształcenie pobranego ciągu znaków na liczbę typu double i zapisanie
jej w zmiennej wartosc. W przypadku błędu wypisanie komunikatu i
wyjście z kodem błędu
-
W przypadku, gdy liczba miała być dodatnia, a jest ujemna, wypisanie błędu
i wyjście z kodem błędu
-
Przypisanie wyniku zmiennej liczba
-
STOP
int licz_funkcja_pochodna(double iks)
Opis: funkcja obliczająca wartości funkcji i jej pochodnej dla zadanego
iks
i przypisująca je do zmiennych globalnych wart_funkcji i wart_pochodnej.
Zmienne:
Nazwa |
Typ |
Opis |
iks |
double |
Wartość x, dla której liczona jest wartość funkcji i pochodnej |
i |
int |
Używana do zwracania kodów błędów |
wynik |
int |
zwracany kod błędu |
Algorytm:
-
START
-
Obliczenie wartości funkcji dla zadanego iks. W przypadku błędu wypisanie
komunikatu i wyjście z funkcji. Wypisanie wyniku
-
Obliczenie wartości pochodnej dla zadanego iks. W przypadku błędu wypisanie
komunikatu i wyjście z funkcji. Wypisanie wyniku
-
STOP
void blad(int i, double value, char liczba[1000], int dodaj)
Opis: funkcja wypisująca błędy napotkane przez program przy obliczaniu
(wzoru) funkcji, jej pochodnej albo wczytywaniu liczby (tzn. są do niej
przekazywane kody błędów z funkcji wartosc_funkcji, wartosc_pochodnej
i wez_liczba)
Zmienne:
Nazwa |
Typ |
Opis |
j,z |
int |
Używane w pętlach |
i |
int |
Kod błędu, który będzie komentowany |
liczba[1000] |
char |
W tej zmiennej jest zawarty pierwotny wzór funkcji podany przez użytkownika |
dodaj |
int |
Ile znaków spacji ma być napisane, gdy program wskazuje konkretne błędne
miejsce we wzorze funkcji |
Algorytm:
-
START
-
jeżeli i==1, wypisanie informacji o zbyt dużej ilości prawych nawiasów
i wskazanie miejsca błędu we wzorze funkcji.
-
jeżeli i==2, wypisanie informacji o dzieleniu przez zero i wskazanie miejsca
błędu (jeżeli jest to możliwe) we wzorze funkcji
-
jeżeli i==3, wypisanie informacji, że w liczbie może być tylko jedna kropka
części dziesiętnej i wskazanie miejsca błędu we wzorze funkcji
-
jeżeli i==4, wypisanie informacji o nieznanej funkcji matematycznej we
wzorze funkcji
-
jeżeli i==5, wypisanie informacji o nieznanym operatorze matematycznym.
Wskazanie nieznanego znaku we wzorze funkcji.
-
jeżeli i==6, wypisanie informacji o zbyt małej ilości pamięci.
-
jeżeli i==7, informacja, że ostatnim znakiem we wzorze funkcji jest nawias
lub znak działania
-
jeżeli i==9, informacja, iż funkcja ln jest zdefiniowana tylko dla liczb
dodatnich
-
jeżeli i==11, informacja, iż minus może się znaleźć tylko na początku liczby
-
jeżeli i>11 lub i==8, wypisanie informacji o błędzie.
-
STOP
int oblicz_poziom(int wartosc)
Opis: Funkcji należy podać wartość wagi działania zapisanego w tablicy
wagi
struktury LICZBA. Funkcja zwróci poziom zagłębienia danej wagi.
Jest wykorzystywana w wartosc_funkcji
Zmienne:
Nazwa |
Typ |
Opis |
i |
int |
Waga, dla której będzie liczony poziom |
zwracana |
int |
Wartość zwracana |
wynik |
int |
zwracany poziom wagi |
Algorytm:
-
START
-
przypisywanie zmiennej zwracana wartości poziomu wagi
-
STOP
int oblicz_dzialanie(int wartosc)
Opis: Funkcji należy podać wartość wagi działania zapisanego w tablicy
wagi
struktury LICZBA. Funkcja zwróci kod działania danej wagi. Jest
wykorzystywana w wartosc_funkcji
Zmienne:
Nazwa |
Typ |
Opis |
i |
int |
Waga, dla której będzie liczone działanie |
zwracana |
int |
Wartość zwracana |
wynik |
int |
zwracany kod działania wagi |
Algorytm:
-
START
-
przypisywanie zmiennej zwracana wartości dzialania wagi
-
STOP
int dodaj_klocek(int numer, unsigned char *string, double liczba, int
waga, LICZBA *mojaliczba)
Opis: Funkcja dodaje jedno działanie na pozycji "numer" do struktury
mojaliczba.
Jest wykorzystywana w wartosc_funkcji
Zmienne:
Nazwa |
Typ |
Opis |
numer |
int |
Pozycja, na której będzie wstawione działanie |
*string |
unsigned char |
Ciąg znakowy działania |
liczba |
double |
Liczba działania |
waga |
int |
Waga działania |
*mojaliczba |
LICZBA |
W tej strukturze będzie przeprowadzona operacja dodawania |
wynik |
int |
zwracany kod błędu |
Algorytm:
-
START
-
Sprawdzenie, czy w zmiennej "mojaliczba" jest miejsce na nowy element:
-
Przypisanie do struktur zmiennej mojaliczba nowego działania:
-
Zwiększenie ilości elementów w strukturze "mojaliczba":
-
STOP
int usun_klocek(int numer, int ileusunac, LICZBA *mojaliczba)
Opis: Funkcja usuwa działania ze struktury mojaliczba. Jest wykorzystywana
w wartosc_funkcji
Zmienne:
Nazwa |
Typ |
Opis |
numer |
int |
Pozycja, od której będą usuwane działania |
ileusunac |
int |
Ile kolejnych działań usunąć |
i |
int |
Używana w pętli |
*mojaliczba |
LICZBA |
W tej strukturze będzie przeprowadzona operacja odejmowania |
wynik |
int |
zwracany kod błędu |
Algorytm:
-
START
-
Sprawdzenie, czy użytkownik nie chce usunąć za dużo działań:
-
Usuwanie elementów:
-
Zmniejszenie ilości elementów w strukturze mojaliczba:
-
STOP
int wartosc_funkcji(unsigned char *ciag, double *zwroc_wartosc, double
iks)
Opis: Funkcja oblicza wartość funkcji o wzorze podanym w ciąg
dla x o wartości podanej w zmiennej iks.
Zmienne:
Nazwa |
Typ |
Opis |
*ciag |
unsigned char |
Wzór funkcji |
mojaliczba |
LICZBA |
wzór przechowywany w postaci zrozumiałej dla programu |
i,j |
int |
Używane w pętlach |
iks |
int |
x, dla którego obliczamy wartość funkcji |
*zwroc_wartosc |
double |
Tutaj jest wzracana wartość funkcji |
num |
int |
Numer "obrabianego" działania w strukturze mojaliczba |
poziom |
int |
Poziom "obrabianego" działania w strukturze mojaliczba |
wynik |
double |
Zmienna pomocnicza przechowująca liczbę pobraną ze struktury mojaliczba |
mnoznik |
double |
Mnożnik dla liczby pobranej ze struktury mojaliczba |
znaki[100] |
char |
Bufor pomocniczy na ciąg znaków ze struktury mojaliczba |
W poniższym opisie przyjęto, iż:
-
"liczba" to ciąg znaków, w którym może wystąpić dowolna ilość cyfr 0-9
oraz (najwyżej raz) kropka.
-
"napis" to ciąg znaków, w którym może wystąpić znak alfabetu (mały lub
duży), kropka lub cyfra
-
"argument działania" to liczba lub napis
-
"działanie" to operator dwuargumentowy mający postać znaku + - * / ^ (
). Każde z działań ma swoją wagę (im wyższa, tym działanie jest ważniejsze).
Obliczanie wag zostało opisane dalej.
W myśl powyższych definicji można przyjąć, iż wzór funkcji to ciąg znaków
składający się z liczb i napisów przedzielanych działaniami. Aby móc obliczyć
wartość danej funkcji dla podanego iks, należy (opis jest uproszczony i
pokazuje ogólny zarys algorytmu - bez sprawdzania błędów, etc.):
-
rozłożyć wzór funkcji na liczby i napisy oraz działania i zapisać je w
odpowiedniej kolejności. Zostało to zrealizowane w ten sposób, iż:
-
wartości liczb i napisów są zapisane dla kolejnych argumentów działań w
tablicach liczby i stringi zmiennej mojaliczba (jest to struktura LICZBA).
-
każde działanie zostało natomiast zapamiętane w tablicy wagi tejże zmiennej
mojaliczba. Przy obliczaniu wagi działania wykonywane są następujące czynności:
-
jeżeli jest to działanie (, wartość zmiennej pomocniczej poziom jest zwiększana
o 1.
-
jeżeli jest to działanie ), wartość zmiennej pomocniczej poziom jest zmniejszana
o 1.
-
jeżeli ostatnim argumentem działania był "napis" i obecne działanie to
(, przypisuje mu się wagę 6. W przeciwnym razie: działaniom + - ^ zostają
tam przypisane odpowiednio wartości 1 - 5:
-
+: 1
-
-: 2
-
*: 3
-
/: 4
-
^: 5
-
Do obliczonej wagi dodaje się wartość zmiennej pomocniczej poziom * 6.
-
ostatniemu elementowi w tablicy wagi w zmiennej mojaliczba przypisuje się
wartość 0
-
wartość napisów "x", "pi" i "e" zamienić na odpowiedającą wartość iks oraz
stałych matematycznych
Następnie wykonywana jest pętla dopóki waga pierwszego elementu tablicy
wagu w zmiennej mojaliczba jest różna od 0:
-
wyszukiwana jest najwyższa waga w tablicy waga w zmiennej mojaliczba. Numer
takiego elementu jest przypisywany do zmiennej pomocniczej num.
-
wykonywane jest działanie między elementami num i num+1 w tablicy liczby
i stringi zmiennej mojaliczba. Wynik działania jest przypisywany do elementu
num tablicy liczby zmiennej mojaliczba. Wartość elementu num+1 w tablicy
wagi jest przypisywana elementowi num. Wszystkie elementy od elementu num+2
w tablicy liczby i znaki są kopiowane do elementu num+1.
Przykład:
wzór
5*ln(x)+2-e
jest rozłożony następująco:
liczby |
znaki |
wagi |
5 |
|
3 |
|
ln |
6+6*1=12 |
|
x |
1 |
2 |
|
2 |
|
e |
0 |
Po zamianie zmiennej iks (iks przykładowo==3) i e tablice wyglądają
następująco:
liczby |
znaki |
wagi |
5 |
|
3 |
|
ln |
6+6*1=12 |
3 |
|
1 |
2 |
|
2 |
2,7 |
|
0 |
Po wykonaniu pierwszego działania tablice wyglądają następująco:
liczby |
znaki |
wagi |
5 |
|
3 |
1,0986 |
|
1 |
2 |
|
2 |
2,7 |
|
0 |
drugiego:
liczby |
znaki |
wagi |
5,49306 |
|
1 |
2 |
|
2 |
2,7 |
|
0 |
itd.
Szczegółowy algorytm:
-
START
-
przypisanie warunków początkowych do zmiennych
-
jeżeli stan==0:
-
jeżeli aktualny znak w zmiennej *ciag to cyfra:
-
przypisanie warunków początkowych dla zmiennych wynik, mnoznik, znaki
-
stan=1
-
jeżeli aktualny znak w zmiennej *ciag jest znakiem alfabetu:
-
przypisanie warunków początkowych dla zmiennych wynik, znaki
-
stan=2
-
jeżeli aktualny znak w zmiennej *ciag to znak działania (+ -
* / ^):
-
jeżeli jest to ostatni znak zmiennej *ciag, wyjdz z błędem
-
dodaj nowe działanie do struktury mojaliczba. Jeżeli jest tam za
mało pamięci, wyjdź z błędem.
-
przejdź do następnego znaku w strukturze ciag
-
jeżeli aktualny znak w zmiennej *ciag to znak otwarcia nawiasu:
-
jeżeli jest to ostatni znak zmiennej *ciag, wyjdz z błędem
-
zwiększ zmienną poziom
wyczyść kolejny element w strukturze mojaliczba i ustaw warunek
początkowy dla zmiennej wynik
-
przejdź do następnego znaku w strukturze ciag
-
jeżeli aktualny znak w zmiennej *ciag to znak zamknięcia nawiasu:
-
jeżeli poziom==0, wyjdz z błędem
-
zmniejsz zmienną poziom
-
przejdź do następnego znaku w zmiennej ciag
-
jeżeli aktualny znak w zmiennej *ciag to spacja:
-
przejdź do następnego znaku w zmiennej ciag
-
jeżeli aktualny znak w zmiennej *ciag nie jest spacją, znakiem otwarcia/zamknięcia
nawiasu, znakiem działania, cyfrą i znakiem alfabetu:
-
wyjdź z błędem
-
jeżeli stan==1:
-
jeżeli aktualny znak w zmiennej *ciag to cyfra:
-
jeżeli mnoznik>=1 (liczba bez części ułamkowej):
-
mnoznik=mnoznik*10
-
wynik=wynik*10+(*ciag-'0')
-
jezeli mnoznik<1 (liczba z częścią ułamkową):
-
wynik=wynik+mnoznik*(*ciag-'0')
-
mnoznik=mnoznik*0.1
-
przejdź do następnego znaku w strukturze ciag
-
jeżeli aktualny znak w zmiennej *ciag to kropka:
-
jeżeli mnoznik>0.9:
-
mnoznik=0.1
-
jeżeli mnożnik <0.9:
-
wyjdź z błędem
-
przejdź do następnego znaku w strukturze ciag
-
jeżeli aktualny znak w zmiennej *ciag to spacja:
-
przejdź do następnego znaku w zmiennej ciag
-
jeżeli aktualny znak w zmiennej *ciag nie jest spacją, cyfrą i kropką:
-
stan==0
-
jeżeli stan==2:
-
jeżeli aktualny znak w zmiennej *ciag jest znakiem alfabetu:
-
dodaj znak ze zmiennej *ciag do zmiennej znaki. Wielką literę zamień na
małą.
-
przejdź do następnego znaku w zmiennej ciag
-
jeżeli aktualny znak w zmiennej *ciag jest znakiem otwarcia nawiasu:
-
dodaj działanie do struktury mojaliczba
-
jeżeli aktualny znak w zmiennej *ciag nie jest znakiem alfabetu:
-
stan==0
-
wróc do 3, jeżeli aktualny znak w zmiennej *ciag nie jest równy
0x00
-
dodaj działanie do struktury mojaliczba
-
i=0;
-
jeżeli !strcmp(mojaliczba.stringi[i],"pi"):
-
przypisz do mojaliczba.liczby[i] wartośc stałej PI
-
wyczyść mojaliczba.stringi[i]
-
jeżeli !strcmp(mojaliczba.stringi[i],"e"):
-
przypisz do mojaliczba.liczby[i] wartośc stałej E
-
wyczyść mojaliczba.stringi[i]
-
jeżeli !strcmp(mojaliczba.stringi[i],"x"):
-
przypisz do mojaliczba.liczby[i] wartośc zmiennej iks
-
wyczyść mojaliczba.stringi[i]
-
wróć do 9, jeżeli i<mojaliczba.ile
-
do num przypisz indeks najwyższej wagi ze struktury mojaliczba,
do j wartość tej wagi
-
do j przypisz działanie wagi zapisanej w j
-
jeżeli j==ST_DODAWANIE || j==ST_ODEJMOWANIE || j==ST_MNOZENIE || j==ST_DZIELENIE
|| j==ST_POTEGOWANIE:
-
jeżeli strcmp(mojaliczba.stringi[num+1],"") wyjdź z błędem
-
do mojaliczba.liczby[num] przypisz wynik działania dwuagrumentowego
między mojaliczba.liczby[num] i mojaliczba.liczby[num+1].
Przy dzieleniu przez 0 wyjdź z błędem
-
mojaliczba.wagi[num]=mojaliczba.wagi[num+1];
-
jeżeli j==ST_DZIALANIE:
-
jeżeli (!strcmp(mojaliczba.stringi[num],"ln")):
-
mojaliczba.liczby[num]=log(mojaliczba.liczby[num+1]);
-
jeżeli mojaliczba.liczby[num] jest niewłaściwa, wyjdź z błędem
-
strcpy(mojaliczba.stringi[num],"");
-
mojaliczba.wagi[num]=mojaliczba.wagi[num+1];
-
jeżeli !strcmp(mojaliczba.stringi[num],"sin") || !strcmp(mojaliczba.stringi[num],"cos")
|| !strcmp(mojaliczba.stringi[num],"tan") || !strcmp(mojaliczba.stringi[num],"ctg"):
-
do mojaliczba.liczby[num] przypisz wynik funkcji zapisanej w mojaliczba.liczby[num+1].
Dla ctg: jeżeli mojaliczba.liczby[num+1]==0, zwróć błąd
-
jeżeli mojaliczba.liczby[num] jest niewłaściwa, wyjdź z błędem
-
strcpy(mojaliczba.stringi[num],"");
-
mojaliczba.wagi[num]=mojaliczba.wagi[num+1];
-
jeżeli mojaliczba.stringi[num] różne od "sin", "cos", "tan", "ctg",
"ln", zwróć błąd
-
usuń działanie na pozycji n+1 ze struktury mojaliczba. Zmniejsz
mojaliczba.ile.
-
wróć do 13, jeżeli mojaliczba.ile!=0
-
*zwroc_wartosc=mojaliczba.liczby[0];
-
STOP
int wartosc_pochodnej(unsigned char *ciag, double *zwroc_wartosc, double
iks)
Opis: Funkcja oblicza wartość pochodnej funkcji o wzorze podanym w ciąg
dla x o wartości podanej w zmiennej iks.
Zmienne:
Nazwa |
Typ |
Opis |
*ciag |
unsigned char |
Wzór funkcji |
i |
int |
Używana do zwracania kodu błędu |
iks |
int |
x, dla którego obliczamy wartość funkcji |
*zwroc_wartosc |
double |
Tutaj jest wzracana wartość funkcji |
wynik, wynik2 |
double |
Zmienne pomocnicze na wartości funkcji |
Ogólny algorytm:
Liczenie pochodnej odbywa się z przyrostu różnicowego:
f'(x)=(f(x+dx)-f(x))/dx
Szczegółowy algorytm:
-
START
-
Obliczenie wartości funkcji dla x = iks+przyrost i przypisanie jej do zmiennej
wynik. W przypadku wystąpienia błędu wyjście z funkcji z kodem błędu.
-
Obliczenie wartości funkcji dla x = iks i przypisanie jej do zmiennej wynik2.
W przypadku wystąpienia błędu wyjście z funkcji z kodem błędu.
-
*zwroc_wartosc=(wynik-wynik2)/przyrost;
-
STOP
int wez_liczba(unsigned char *ciag, double *wartosc)
Opis: Z ciągu *ciag wyciąga liczbę rzeczywistą i zapisuje w zmiennej
*wartosc.
Zmienne:
Nazwa |
Typ |
Opis |
*ciag |
unsigned char |
Z tego ciągu wyciągamy liczbę |
*wartosc |
double |
Wartość zwracana |
wynik |
double |
Liczba podczas wczytywania liczby z ciągu |
mnoznik |
double |
Mnożnik dla kolejnej cyfry liczby wczytywanej z ciągu |
num |
int |
Numer sprawdzanego znaku w ciągu znaków |
minus |
int |
Czy jest już liczba ujemna (wartość 1) czy nie (wartość!=1) |
wynik |
int |
zwracany kod błędu |
Ogólny algorytm:
Dla każdego znaku w ciągu znaków ciag sprawdzane jest, czy może
się znaleźć w prawidłowo napisanej liczbie (tzn. dopuszczona jest dowolna
ilość cyfr, dokładnie jeden znak kropki, spacje oraz (wyłącznie jako pierwszy
znak) dokładnie raz znak minus). Zgodnie z nimi odpowiednio modyfikowana
jest wartość liczby wynik.
Szczegółowy algorytm:
-
START
-
jeżeli aktualny znak w zmiennej *ciag to cyfra:
-
jeżeli mnoznik>=1 (liczba bez części ułamkowej):
-
mnoznik=mnoznik*10
-
wynik=wynik*10+(*ciag-'0')
-
jezeli mnoznik<1 (liczba z częścią ułamkową):
-
wynik=wynik+mnoznik*(*ciag-'0')
-
mnoznik=mnoznik*0.1
-
przejdź do następnego znaku w strukturze ciag. Zwiększ num
-
jeżeli aktualny znak w zmiennej *ciag to minus:
-
jeżeli num!=0 wyjdź z błędem
-
minus=1
-
przejdź do następnego znaku w strukturze ciag. Zwiększ num
-
jeżeli aktualny znak w zmiennej *ciag to kropka:
-
jeżeli mnoznik>0.9:
-
mnoznik=0.1
-
jeżeli mnożnik <0.9:
-
wyjdź z błędem
-
przejdź do następnego znaku w strukturze ciag. Zwiększ num
-
jeżeli aktualny znak w zmiennej *ciag to spacja:
-
przejdź do następnego znaku w zmiennej ciag. Zwiększ num
-
jeżeli aktualny znak w zmiennej *ciag nie jest spacją, cyfrą i kropką
i minusem:
-
wyjdź z błędem
-
jeżeli aktualny znak w zmiennej *ciąg !=0x00, wróć do 2
-
*wartosc=wynik;
-
jeżeli minus==1 *wartosc=-wynik;
-
STOP
6. Testowanie projektu
Projekt został przetestowany na różnych zestawach danych (przykładowe
dwa są zawarte w katalogu /Docs).
Podczas testowania zwracano uwagę na eliminację błędów numerycznych
oraz na odporność na różnego rodzaju niewłaściwe dane. Usunięto wszelkie
znalezione usterki.
Wiele procedur zawiera obecnie różne informacje debugujące pozwalające
kontrolować poprawność ich działania. Aby je uaktywnić, należy w pliku
config.h usunąć komentarz z linii "#define DEBUG" i skompilować projekt
ponownie.