D/Podstawowe operacje matematyczne

< D

Podstawowe operacje matematyczneEdytuj

Podstawowych operacji matematycznych używaliśmy już od w pierwszych ćwiczeniach i przykładach. Pora na bardziej szczegółowe omówienie, dostępnych operacji na liczbach całkowitych i rzeczywistych.

Arytmetyka liczb całkowitychEdytuj

Zakładamy, że mamy dostępne zmienne x, y, z, tzn.

int x, y, z;

albo

double x, y, z; 

lub innego typu liczbowego.

Najprostszą operacją, jest przypisanie jednej zmiennej do drugiej.

x = y;

przypisuje zmiennej x wartość zmiennej y. Zmienna y, nadal ma poprzednią wartość

Operacje tą można przeprowadzić na zmiennych praktycznie dowolnego typu, nie tylko liczbowych.

Poza tym, prawa strona tego przypisania nie musi być zmienną, może być innym wyrażeniem:

x = -y;
x = y*y;
x = y + z;
x = -x;  // możemy odwoływać się do zmiennych, które nadpisujemy po lewej.

To jakie wyrażenia są dostępne w D, opisują poniższe przykłady.

x + y

zwraca sumę x i y


x - y

zwraca różnicę x i y

x * y

zwraca iloczyn x i y

x / y

zwraca iloraz x i y, lub wynik dzielenia całkowitego gdy x i y są typu całkowitego. Na przykład instrukcja:

float a = 3/2;

zainicjuje zmienną a wartością 1 a nie 1.5 . Można to zmienić zamieniając jeden z elementów wyrażenia na literał typu zmiennoprzecinkowego dopisując kropkę, lub dla przejrzystości .0:

float a = 3./2;
float a = 3.0/2.0;

W przypadku, gdy chcemy podzielić zmienne typu całkowitego możemy zastosować rzutowanie typów

int a = 3;
int b = 2;
float c = a/b;  // c ma wartosc 1
float d = (cast(float)a)/b;   // d ma wartosc 1.5

Zobacz więcej w rozdziale o zmiennych i rzutowaniu.

x % y

zwraca resztę z dzielenia x przez y

Istnieją również operatory składające się z operatora przypisania i operatora działania matematycznego(+=,*=, etc.) działającego analogicznie do

x += y;

równoznacznego z

x = x + y;

przy czym jeśli x nie jest zmienną, tylko jakimś wyrażeniem (np. funkcją zwracającą wskaźnik, który odczytujemy), to będzie obliczone tylko raz.

Podobnie

x -= y;
x = x - y;
x *= y;
x = x * y;

i tak dalej.

Dodatkowo ponieważ operacja zwiększania i zmniejszania zmiennej o 1, jest bardzo częsta, to zamiast pisać:

x = x + 1;

czy

x += 1;

można użyć

x++;

lub

++x;

Podobnie, dla operacji zmniejszania o jeden, użyjemy

x--;

lub

--x;

W operacjach matematycznych możemy używać nawiasów, a operatory matematyczne zachowują standardową kolejność operacji:

x = y + 2 * z;

co jest równoważne

x = y + (2 * z);

Inne niż,

x = (y + 2) * z;

W przypadku długich wyrażeń, warto używać nawiasów albo zmiennych pomocniczych, aby kod był bardziej przejrzysty.

W szczególności, należy uważać na operator dzielenia. Na przykład

x = y / 2*z;    // raczej nie to czego oczekujesz!

oznacza dokładniej to:

x = y / 2 * z;
x = (y / 2) * z;

czyli w zasadzie to samo co

x = (z * y) / 2;

coś innego, niż można by oczekiwać z czytanego kodu. Pamiętaj, więc o umieszczeniu nawiasów

x = y / (2*z);

lub stosowaniu dalej dzielenia:

x = y / 2 / z;
x = (y/2) / z;

W języku D, wszystkie powyższe instrukcje (nawet jeśli zawierają operator przypisania), same w sobie tez są wyrażeniami. Tzn. można ich dalej użyć jako wartości:

x = y = z;

co można zrozumieć jako

x = (y = z);

Wartość zmiennej z zostanie przypisana do zmienne y, a następnie wartość zmiennej y (która teraz ma już wartość zmiennej z), zostanie przypisana do zmiennej x.

Można też tak czynić z bardziej skomplikowanymi operacjami:

x = ++y; // zwiększ y o jeden, a następnie do x zapisz wartość x, jaka była PO tym zwiększeniem
         // tzn. powiększenie wartości dokonuje się PRZED pobraniem wartości wyrażenia, tzw. preinkrementacja
x = y++; // zwiększ y o jeden, a następnie do x zapisz wartość x, jaka była zanim powiększono y o jeden
         // tzn. najpierw zostanie odczytana wartość y, i ona przypisana do x, a potem y zwiększone o 1, tzw. postinkrementacja
x = (y = x); // zapisz wartość x do y, a potem z powrotem zapisz ją do x
x = 3 * y += 2;  // zwiększ y o 2, i nową wartość y pomnożoną przez 3 zapisz do x

Należy mieć na uwadze, że język D, umożliwia na wielokrotne odwoływanie się do tej samej zmiennej w wyrażeniach i instrukcjach. Może to niestety przynieść bardzo nie oczekiwane i trudne w przewidzeniu skutki:

x = x++ + ++ x + x++ + x;  // ?
y = (y += 2) + (y *= 2);  // ?

Szczegółowe zasady obliczania tych wyrażeń są dostępne w dokumentacji językach, jednak w wielu przypadkach język nie gwarantuje żadnego określonego porządku wykonywanych operacji. Są to tak zwane wyrażenia z skutkami ubocznymi (ang. side effects). Należy się wystrzegać taki operacji.


Funkcje dodatkoweEdytuj

Dodatkowo w module std.math, znajduje się wiele użytecznych funkcji na liczbach.

log10(x)
writeln(cast(int) log10(1234) + 1); //4

zwraca logarytm dziesiętny z x, ciekawą własnością tego logarytmu jest to, że po odcięciu części ułamkowej (nie zaokrągleniu!) i dodaniu 1 otrzymamy ilość cyfr w x.

tan(x), cos(x), sin(x)

Funkcje trygonometryczne, kolejno tangens, cosinus i sinus. Brakuje cotangensa, ale można go łatwo wyliczyć wiedząc że ctg(x) = 1 / tan(x). Należy pamiętać że x jest kątem w radianach.

sqrt(x)

zwraca pierwiastek kwadratowy z x.

abs(x)

zwraca wartość bezwzględną (ang. absolute) z x.

pow(x,y)

zwraca x podniesiony do potęgi y. (ang. power, pow).

int x = pow(2, 3); // 8
double x = pow(2.0, 1.0/3.0); // pierwiastek sześcienny z 2

Należy tutaj jednak zaznaczyć, że pow, oczekuje albo dwóch liczb całkowitych, lub dwóch liczb rzeczywistych, mieszanie typów, może doprowadzić do błędu kompilacji. Można wymusić konkretne typy, poprzez rzutowanie, albo przypisanie do zmiennych tymczasowych:

double a = 200;
double b = 0.5;
int c = cast(int)pow(a, b);

W wersji 2 język D, można użyć operatora ^^, do podobnego celu. int x = y ^^ 5; obliczy piątą potęgę y, podobnie jak pow(y, 5).

Pełna lista funkcji z modułu std.math – klik.

Inne operacjeEdytuj

Poza operacjami arytmetycznymi. Na liczbach można wykonywać operacje porównania i porządku. Operacje te zwracają wartościach boolowską (prawda lub fałsz), i mówią czy porównanie lub porządek jest zachowany czy nie. Operacje te są bardzo przydatne w instrukcjach warunkowych i pętlach, jak również przy sprawdzaniu czy dane z plików lub klawiatury są poprawnego typu.

Operatory porównaniaEdytuj

x == y

zwraca true wtedy i tylko wtedy, gdy x jest równe y

x != y

zwraca truee wtedy i tylko wtedy, gdy x jest różne od y

Operatory porządkuEdytuj

x > y

zwraca wartość true wtedy i tylko wtedy, gdy x jest większe od y

x >= y

zwraca wartość true wtedy i tylko wtedy, gdy x jest większe lub równe od y

x < y

zwraca wartość true wtedy i tylko wtedy, gdy x jest mniejsze od y

x <= y

zwraca wartość true wtedy i tylko wtedy, gdy x jest mniejsze lub równe y

Dodatkowo język D, dla typów zmiennoprzecinkowych udostępnia specjalne operatory porównania i porządku, użyteczne przy posługiwaniu się wartościami NaN, oraz związane z pewnymi ważnymi niuansami ich działania.

Operacje logiczneEdytuj

Na zmiennych boolowskich (zobacz poprzedni rozdział o typach zmiennych) można wykonywać operacje logiczne.

x && y

zwraca wartość logiczną wyrażenia x i y (innymi słowy zwraca true wtedy i tylko wtedy gdy obie zmienne mają wartość true)

x || y

zwraca wartość logiczną wyrażenia x lub y (zwraca wartość true wtedy, gdy jedna lub obie wartości mają wartość true)

x ^^ y

zwraca wartość logiczną wyrażenia x albo y (zwraca wartość true wtedy i tylko wtedy gdy jedna z nich ma wartość true)

!x

zwraca wartość logiczną wyrażenia nie x (zwraca false dla x = true i true dla x = false)

Operatory logiczne, podobnie jak arytmetyczne, można łączyć. W szczególności, można je łączyć między sobą oraz z operatorami porównania i porządku.

Na przykład:

bool t = u && x && y && !z;
bool ok = a < b && c < b;  // t jest prawdą, jeśli a jest mniejsze od b oraz c, równocześnie.

Operatory bitoweEdytuj

Liczby całkowite w D, posiadają dodatkowo operacje bitowe. Wartość wynikowa, ma bity które mają wartości analogiczne do zastosowania analogicznej funkcji logicznej na każdym bicie. Na przykład, 19 bit wartości x & y, ma wartość odpowiadającą wyrażeniu x19 && y19, gdzie x19 i y19 to bit 19 z x oraz y odpowiednio.

x & y

zwraca wartość składającą się z wartości logicznych koniunkcji kolejnych bitów składających się na x i y

x | y

zwraca wartość składającą się z wartości logicznych alternatyw kolejnych bitów składających się na x i y

x ^ y

zwraca wartość składającą się z wartości logicznych alternatyw wykluczających kolejnych bitów składających się na x i y

Priorytety operatorówEdytuj

Priorytet Opis Operatory Komentarz
15

Największy

Szablon instancji !
14.5 Abstrakcja lambd => Nie jest prawdziwym operatorem, generowane automatycznie
14 Postfixowe operatory . ++ -- ( [ ( i [ wymagają odpowiedniego nawiasu zamykającego
13 Operator potęgi ^^ Prawostronny
12 Jednoargumentowe operatory & ++ -- * + - ! ~ cast
11 * / %
10 + - ~ Dwuargumentowy '~' jest operatorem powiązania, łączenie ciągów znaków, tablic itp.
9 Przesunięcia bitowe << >> >>>
6a Operatory porównania == != > < >= <= !> !< !>= !<= <> !<> <>= !<>= in !in is !is Nieuporządkowane względem operatorów bitowych
8b Bitowe AND & Nieuporządkowane względem operatorów porównania
7b Bitowe XOR ^ Nieuporządkowane względem operatorów porównania
6b Bitowe OR | Nieuporządkowane względem operatorów porównania
5 Logiczne AND &&
4 Logiczne OR ||
3 Operator warunkowy ?: Prawostronny
2 Operatory przypisania = -= += <<= >>= >>>= = *= %= ^= ^^= ~= Prawostronny
1.5 Abstrakcja lambd => Nie jest prawdziwym operatorem, generowane automatycznie
1 Operator przecinka ,
0

Najmniejszy

Separator zakresu .. Nie jest prawdziwym operatorem, część składni języka D

Jak widać nawiasy są bardzo wysoko i można ich używać do porządkowania kolejności wykonania innych operatorów, np. w 2+2^^2 możemy wymusić dodanie przed potęgowaniem przez zapis (2+2)^^2, dzięki czemu wynik nie będzie wynosił 6 a 16.

Promocja typówEdytuj

W języku D mogą zachodzić następujące promocje typów całkowitych:

Z typu Do typu
bool int
byte int
ubyte int
short int
ushort int
char int
wchar int
dchar uint

Każdy typ numeryczny całkowity może też uzyskać promocję do float i double.

Lvalue i rvalueEdytuj

Po polsku L-wartości i R-wartości. Wbrew pozorom to bardzo proste zagadnienie.

  • Lvalue – istniejąca zmienna lub referencja do niej, można przypisać jej wartość, pobierać adres w pamięci (czyli m.in tworzyć do niej inne referencje) itd., z reguły stoi po lewej stronie wyrażenia, zawsze w przypadku przypisania.
  • Rvalue – istnieje tylko przez chwilę w trakcie obliczania wartości wyrażenia. Przykładem może być 2 lub x^^3, obie te wartości w momencie trwania wyrażenia istnieją tylko tymczasowo, nie mają adresu w pamięci ani nie można nic do nich przypisać. Dlatego zapis 2x=5.0; nie jest poprawny, ponieważ w rzeczywistości nie sprowadza się do x=5.0/2; a np. 3*2=5.0, co już na pierwszy rzut oka nie ma sensu.

ĆwiczeniaEdytuj

Co wyświetlą poniższe fragmenty programów:

 int x = 5;
 int y = 101;
 int z = x * y+y / x;
 writefln("z=%d", z);
 int x = 5;
 int y = 101;
 x = x--;
 y = ++y;
 writefln("x=%d y=%d", x, y);
 int x = 5;
 int y = 101;
 x = (y += 7) - (x += 3);
 writefln("x=%d y=%d", x, y);
 int x = 5;
 int y = 101;
 int z = x+++y++;
 writefln("x=%d y=%d z=%d", x, y, z);

DodatkoweEdytuj

 int x = 5;
 int y = 5;
 int a = x++ * x++;
 int b = ++y * y++;
 writefln("x=%d y=%d a=%d b=%d", x, y, a, b);

Czy masz gwarancje, że wynik będzie zawsze taki sam? Zajrzyj do dokumentacji języka, aby się upewnić!