C++ bez cholesterolu

Użytki matematyczne

Wstęp

Do użytków matematycznych należałoby zaliczyć na pewno nagłówek C, <cmath>. Zawiera on sporo dobrze znanych, a zarazem często mało użytecznych funkcji matematycznych. Nie ma też w nich niczego niezwykłego, ani skomplikowanego, zatem pozwolę sobie, z uwagi na "wrodzone lenistwo", pominąć ich opis. :)

To, o czym chciałbym tutaj wspomnieć to typy zespolone i macierze. Zwłaszcza to drugie jest mocno skomplikowanym tematem.

Complex

Klasa complex powstała jako jeden z najwcześniejszych (jeśli nie liczyć iostream) części biblioteki standardowej C++. Początkowo była to jednak zwykła klasa z polami typu double. Później samo complex stało się wzorcem, którego paramtetr był typem owych pól. Ponieważ jednak pola klasy complex winny być typu zmiennoprzecinkowego, zatem dostarczono również jawnie specjalizowane typy, float_complex, double_complex i long_double_complex.

Klasa liczb zespolonych, jak wiadomo, ma implementować operacje na liczbach zespolonych, posiada zatem swoją część rzeczywistą i urojoną oraz różne operacje, które się zwykle na takich liczbach wykonuje (wraz z przeciążonymi operatorami). Mamy zatem przede wszystkim:

Co do operatorów, to tu niczego niezwykłego nie ma. Na dodatek, jak widać, nie wszystkie operatory mają sens (zwracam uwagę, że pod uwagę jest sens brać tylko te operatory, które istnieją dla wartości zmiennoprzecinkowych). Porównanie jest zdefiniowane nie tylko między typami complex, ale również między complex a typem parametryzującym (czyli między liczbą zespoloną a rzeczywistą). Polega to oczywiście na potraktowaniu liczby rzeczywistej jak liczby zespolonej o części urojonej równej zero.

Nie ma takoż operatorów porządkowania liczb zespolonych; ja przynajmniej zresztą nie znam żadnej definicji porównywania liczb zespolonych. Na uwagę jednak zasługują funkcje specjalizowane, czyli:

Do tego oczywiście mamy jeszcze przeciążony operator << do wyprowadzania danych do strumienia. Wyprowadza się je w postaci "(re,im)". Jest też operator >>, który pozwala z kolei wprowadzić ze strumienia liczbę zespoloną, pod warunkiem, że jest takiej właśnie postaci.

Co do funkcji matematycznych działających na liczbach zespolonych, to należą do nich: sin, cos, tan, sinh, cosh, tanh, exp, pow, log, log10 i sqrt (ta ostatnia mnie, szczerze mówiąc, lekko zdumiała; mnie uczono na studiach, że pierwiastek z liczby zespolonej ma tyle wyników, ile wynosi stopień pierwiastka, a tu - proszę, jest jeden, konkretny wynik!).

Samo "std::complex" jest oczywiście wzorcem, który specjalizuje się typem zmiennoprzecinkowym. Oczywiście wcale to nie oznacza, że można do tego celu użyć typów float, double i long double. Można dowolnego, który spełnia koncept liczby rzeczywistej. Niestety biblioteka standardowa nie definiuje konceptu liczby rzeczywistej. Tak mniej więcej "na oko", oglądając plik nagłówkowy można się zorientować, że typ taki powinien mieć dostępne wzystkie operatory, jak przystało na liczbę rzeczywistą, a dodatkowo posiadać jeszcze konstruktor bezparametrowy, który inicjalizuje obiekt wartością zerową, a także jeden, który przyjmuje typ double jako argument (typ double jest, przypominam, typem literału zmiennoprzecinkowego).

Valarray

Typ valarray powinien się tak naprawdę nazywać vector (co przyznaje sam Bjarne Stroustrup); niestety różne okoliczności sprawiły, że vector ten z STL był pierwszy, więc wyszło jak wyszło. Typ valarray jest dokładnie czymś, co w matematyce określa się jako wektor. Typ ten głównie ma zastosowanie w złożonych obliczeniach numerycznych z zastosowaniem agresywnych technik optymalizacji. Jedną z istotniejszych rzeczy jest to, że dla valarray zakłada się brak tzw. aliasingu (kompilator ma prawo założyć, że dwa różne obiekty valarray nie mają żadnych wspólnych części).

Obiekt typu valarray ma konstruktory identyczne jak typ vector (poza tym z dwoma iteratorami). Podobnie jest z operatorami przypisania i jedną z wersji operatora []. Poza tym, valarray dostarcza kilka ciekawych postaci operatora [], z użyciem typów pomocniczych gslice_array, slice_array, mask_arrayindirect_array. Do uzyskania dwóch pierwszych służą również typy pomocnicze gslice i slice.