C/Zaawansowane operacje matematyczne
Z Wikibooks, biblioteki wolnych podręczników.
< C
Spis treści |
[edytuj] Biblioteka matematyczna
Aby móc korzystać z wszystkich dobrodziejstw funkcji matematycznych musimy na początku dołączyć plik math.h:
#include <math.h>
A w procesie kompilacji (dotyczy kompilatora GCC) musimy niekiedy dodać flagę "-lm":
gcc plik.c -o plik -lm
Funkcje matematyczne, które znajdują się w bibliotece standardowej możesz znaleźć tutaj. Przy korzystaniu z nich musisz wziąć pod uwagę m.in. to, że biblioteka matematyczna prowadzi kalkulację w oparciu o radiany a nie stopnie.
[edytuj] Stałe matematyczne
W pliku math.h zdefiniowane są pewne stałe, które mogą być przydatne do obliczeń. Są to m.in.:
- M_E - podstawa logarytmu naturalnego (e, liczba Eulera)
- M_LOG2E - logarytm o podstawie 2 z liczby e
- M_LOG10E - logarytm o podstawie 10 z liczby e
- M_LN2 - logarytm naturalny z liczby 2
- M_LN10 - logarytm naturalny z liczby 10
- M_PI - liczba π
- M_PI_2 - liczba π/2
- M_PI_4 - liczba π/4
- M_1_PI - liczba 1/π
- M_2_PI - liczba 2/π
[edytuj] Prezentacja liczb rzeczywistych w pamięci komputera
Być może ten temat może wydać Ci się niepotrzebnym, lecz w wielu książkach nie ma w ogóle tego tematu. Dzięki niemu zrozumiesz, jak komputer radzi sobie z przecinkiem oraz dlaczego niektóre obliczenia dają niezbyt dokładne wyniki. Na początek trochę teorii: do przechowywania liczb rzeczywistych przeznaczone są 3 typy: float, double oraz long double. Zajmują one odpowiednio 32, 64 oraz 80 bitów. Wiemy też, że komputer nie ma fizycznej możliwości zapisania przecinka. Spróbujmy teraz zapisać jakąś liczbę wymierną w formie liczb binarnych. Nasza liczba to powiedzmy 4.25. Spróbujmy ją rozbić na sumę potęg dwójki: 4 = 1*22 + 0*21+0*20. Dobra - rozpisaliśmy liczbę 4, ale co z częścią dziesiętną? Skorzystajmy z zasad matematyki - 0.25 = 2-2. Zatem nasza liczba powinna wyglądać tak:
- 100.01
Ponieważ komputer nie jest w stanie przechować pozycji przecinka, ktoś wpadł na prosty ale sprytny pomysł ustawienia przecinka jak najbliżej początku liczby i tylko mnożenia jej przez odpowiednią potęgę dwójki. Taki sposób przechowywania liczb nazywamy zmiennoprzecinkowym, a proces przekształcania naszej liczby z postaci czytelnej przez człowieka na format zmiennoprzecinkowy nazywamy normalizacją. Wróćmy do naszej liczby - 4.25. W postaci binarnej wygląda ona tak: 100.01, natomiast po normalizacji będzie wyglądała tak: 1.0001*22. W ten sposób w pamięci komputera znajdą się dwie informacje: liczba zakodowana w pamięci z "wirtualnym" przecinkiem oraz numer potęgi dwójki. Te dwie informacje wystarczają do przechowania wartości liczby. Jednak pojawia się inny problem - co się stanie, jeśli np. będziemy chcieli przełożyć liczbę typu
? Otóż tutaj wychodzą na wierzch pewne niedociągnięcia komputera w dziedzinie samej matematyki. 1/3 daje w rozwinięciu dziesiętnym 0.(3). Jak zatem zapisać taką liczbę? Otóż nie możemy przechować całego jej rozwinięcia (wynika to z ograniczeń typu danych - ma on niestety skończoną liczbę bitów). Dlatego przechowuje się tylko pewne przybliżenie liczby. Jest ono tym bardziej dokładne im dany typ ma więcej bitów. Zatem do obliczeń wymagających dokładnych danych powinniśmy użyć typu double lub long double. Na szczęście w większości przeciętnych programów tego typu problemy zwykle nie występują. A ponieważ początkujący programista nie odpowiada za tworzenie programów sterujących np. lotem statku kosmicznego, więc drobne przekłamania na odległych miejscach po przecinku nie stanowią większego problemu.
Na ile poważny jest to problem? Spróbujmy przyjrzeć się działaniu, polegającym na 1000-krotnym dodawaniu do liczby wartości 1/3. Oto kod:
#include <stdio.h> int main () { float a = 0; int i = 0; for (;i<1000;i++) { a += 1.0/3.0; } printf ("%f\n", a); }
Z matematyki wynika, że 1000*(1/3) = 333.(3), podczas gdy komputer wypisze wynik, nieco różniący się od oczekiwanego (w moim przypadku):
333.334106
Błąd pojawił się na cyfrze części tysięcznej liczby. Nie jest to może poważny błąd, jednak zastanówmy się, czy ten błąd nie będzie się powiększał. Zamieniamy w kodzie ilość iteracji z 1000 na 100 000. Tym razem mój komputer wskazał już nieco inny wynik:
33356.554688
Błąd przesunął się na cyfrę dziesiątek w liczbie. Tak więc nie należy do końca polegać na prezentacji liczb zmiennoprzecinkowych w komputerze.
[edytuj] Liczby zespolone
Operacje na liczbach zespolonych są częścią uaktualnionego standardu języka C o nazwie C99, który jest obsługiwany jedynie przez część kompilatorów
| Podane tutaj informacje zostały sprawdzone na systemie Gentoo Linux z biblioteką GNU libc w wersji 2.3.5 i kompilatorem GCC w wersji 4.0.2 |
Dotychczas korzystaliśmy tylko z liczb rzeczywistych, lecz najnowsze standardy języka C umożliwiają korzystanie także z innych liczb - np. z liczb zespolonych.
Aby móc korzystać z liczb zespolonych w naszym programie należy w nagłówku programu umieścić następującą linijkę:
#include <complex.h>
Wiemy, że liczba zespolona zdeklarowana jest następująco:
z = a+b*i, gdzie a, b są liczbami rzeczywistymi, a i*i=(-1).
W pliku complex.h liczba i zdefiniowana jest jako I. Zatem wypróbujmy możliwości liczb zespolonych:
#include <math.h> #include <complex.h> #include <stdio.h> int main () { float _Complex z = 4+2.5*I; printf ("Liczba z: %f+%fi\n", creal(z), cimag (z)); return 0; }
następnie kompilujemy nasz program:
gcc plik1.c -o plik1 -lm
Po wykonaniu naszego programu powinniśmy otrzymać:
Liczba z: 4.00+2.50i
W programie zamieszczonym powyżej użyliśmy dwóch funkcji - creal i cimag.
- creal - zwraca część rzeczywistą liczby zespolonej
- cimag - zwraca część urojoną liczby zespolonej