Operații aritmetice
Operațiile aritmetice se efectuează asupra numerelor. Valorile care participă la operație se numesc operanzi. În limbajul de programare C++, operațiile aritmetice pot fi binare (efectuate asupra a doi operanzi) și unare (efectuate asupra unui singur operand). La operațiile binare se includ următoarele:
- + (Operația de adunare returnează suma a două numere):
int a {10};
int b {7};
int c {a + b}; // 17
int d {4 + b}; // 11
În acest exemplu, rezultatul operațiilor este utilizat pentru inițializarea variabilelor, dar putem folosi și operația de atribuire pentru a seta valoarea variabilelor:
int a {10};
int b {7};
int c = a + b; // 17
int d = 4 + b; // 11
- - (Operația de scădere returnează diferența a două numere):
int a {10};
int b {7};
int c {a - b}; // 3
int d {4 - b}; // -3
- * (Operația de înmulțire returnează produsul a două numere):
int a {10};
int b {7};
int c {a * b}; // 70
int d {4 * b}; // 28
- / (Operația de împărțire returnează câtul a două numere):
int a {26};
int b {5};
int c {a / b}; // c = 5
int d {4 / b}; // d = 0
La împărțire trebuie să fim atenți, deoarece dacă în operație participă două numere întregi, partea zecimală (dacă există) va fi ignorată, chiar dacă rezultatul este atribuit unei variabile de tip float sau double:
#include <iostream>
int main()
{
int a {26};
int b {5};
float c {a / b}; // c = 5
double d {4 / b}; // d = 0
std::cout << "c = " << c << std::endl;
std::cout << "d = " << d << std::endl;
}
Pentru ca rezultatul să fie un număr cu virgulă mobilă, unul dintre operanzi trebuie să fie, de asemenea, un număr cu virgulă mobilă:
#include <iostream>
int main()
{
float a {26};
int b {5};
float c {a / b}; // c = 5.2
double d {4.0 / b}; // d = 0.8
std::cout << "c = " << c << std::endl;
std::cout << "d = " << d << std::endl;
}
- % (Operația de obținere a restului împărțirii întregi):
int a {26};
int b {5};
int c {a % b}; // c = 26 % 5 = 26 - 5 * 5 = 1
int d {4 % b}; // d = 4 % 5 = 4
Unele particularități în lucrul cu numere cu virgulă mobilă
La adunarea sau scăderea numerelor cu virgulă mobilă care diferă semnificativ ca valoare, trebuie să fim precauți. De exemplu, să adunăm numărul 1.23E-4 (adică 0.000123) și 3.65E+6 (adică 3650000). Ne așteptăm ca suma să fie 3650000,000123. Însă la conversia într-un număr cu virgulă mobilă cu precizie de șapte cifre, aceasta devine următoarea:
3.650000E+06 + 1.230000E-04 = 3.650000E+06
Sau codul corespunzător în C++:
#include <iostream>
int main()
{
float num1{ 1.23E-4 }; // 0.000123
float num2{ 3.65E+6 }; // 3650000
float sum {num1 + num2}; // sum =3.65e+06
std::cout << "sum =" << sum << "\n";
}
Adică primul număr nu s-a schimbat în niciun fel, deoarece pentru stocarea preciziei sunt alocate doar 7 cifre.
De asemenea, merită menționat că standardul IEEE, implementat de compilatoarele C++, definește valori speciale pentru numerele cu virgulă mobilă, în care mantisa, la nivel binar, constă doar din zerouri, iar exponentul, format din uni, în funcție de semn, reprezintă valorile +infinity (plus infinit +∞) și -infinity (minus infinit −∞). Astfel, la împărțirea unui număr pozitiv la zero, rezultatul va fi +infinity, iar la împărțirea unui număr negativ la zero – -infinity.
O altă valoare specială cu virgulă mobilă, definită de acest standard, este NaN (Not a Number – nu este un număr). Această valoare apare ca rezultat al unei operații care nu este definită matematic, de exemplu când zero se împarte la zero sau infinit la infinit. Rezultatul oricărei operații în care unul sau ambii operanzi sunt NaN va fi de asemenea NaN.
Pentru demonstrație, să analizăm următorul program:
#include <iostream>
int main()
{
double a{ 1.5 }, b{}, c{}, d {-1.5};
double result { a / b };
std::cout << a << "/" << b << " = " << result << std::endl;
result = d / c;
std::cout << d << "/" << c << " = " << result << std::endl;
result = b / c;
std::cout << b << "/" << c << " = " << result << std::endl;
std::cout << result << " + " << a << " = " << result + a << std::endl;
}
În expresia a / b, numărul 1.5 este împărțit la 0, deci rezultatul va fi plus infinit.
În mod similar, în expresia d / c, numărul -1.5 este împărțit la 0, deci rezultatul va fi minus infinit.
În expresia b / c, 0 este împărțit la 0, deci rezultatul va fi NaN.
Prin urmare, ultima expresie result + a este echivalentă cu NaN + 0, deci rezultatul va fi tot NaN.
Ieșirea în consolă a programului:
1.5/0 = inf
-1.5/0 = -inf
0/0 = nan
nan + 1.5 = nan
Increment și decrement
Există și două operații aritmetice unare, care se aplică asupra unui singur număr: ++ (increment) și -- (decrement). Fiecare dintre aceste operații are două forme: prefixată și postfixată:
- Incrementul prefixat - Mărește valoarea variabilei cu unu, iar rezultatul obținut este utilizat ca valoarea expresiei ++x.
#include <iostream>
int main()
{
int a {8};
int b {++a};
std::cout << "a = " << a << std::endl; // a = 9
std::cout << "b = " << b << std::endl; // b = 9
}
- Incrementul postfixat - Mărește valoarea variabilei cu unu, dar valoarea expresiei x++ va fi cea pe care variabila o avea înainte de incrementare.
#include <iostream>
int main()
{
int a {8};
int b {a++};
std::cout << "a = " << a << std::endl; // a = 9
std::cout << "b = " << b << std::endl; // b = 8
}
- Decrementul prefixat - Scade valoarea variabilei cu unu, iar valoarea obținută este utilizată ca valoarea expresiei --x.
#include <iostream>
int main()
{
int a {8};
int b {--a};
std::cout << "a = " << a << std::endl; // a = 7
std::cout << "b = " << b << std::endl; // b = 7
}
- Decrementul postfixat - Scade valoarea variabilei cu unu, dar valoarea expresiei x-- va fi cea pe care variabila o avea înainte de decrementare.
#include <iostream>
int main()
{
int a {8};
int b {a--};
std::cout << "a = " << a << std::endl; // a = 7
std::cout << "b = " << b << std::endl; // b = 8
}
Prioritatea și asociativitatea operatorilor
Operatorii pot fi asociativi la stânga – astfel de operatori se execută de la stânga la dreapta – și asociativi la dreapta – se execută de la dreapta la stânga. Marea majoritate a operatorilor sunt asociativi la stânga (de exemplu, operațiile aritmetice binare), astfel că majoritatea expresiilor sunt evaluate de la stânga la dreapta. Operatorii asociativi la dreapta includ toți operatorii unari, diverși operatori de atribuire și operatorul condițional.
În plus, unele operații au o prioritate mai mare decât altele și, prin urmare, sunt executate mai întâi. Operațiile, în ordinea descrescătoare a priorității:
- ++ (increment), -- (decrement)
- * (înmulțire), / (împărțire), % (restul împărțirii)
- + (adunare), - (scădere)
Prioritatea operatorilor trebuie luată în considerare la executarea unor expresii aritmetice compuse:
int a = 8;
int b = 7;
int c = a + 5 * ++b; // 48
Deși operațiile se execută de la stânga la dreapta, mai întâi va fi executată operația de incrementare ++b, care va crește valoarea variabilei b și o va returna ca rezultat, deoarece această operație are o prioritate mai mare. Apoi se execută înmulțirea 5 * ++b, iar abia la final se execută adunarea a + 5 * ++b.
Trebuie avut în vedere că, dacă într-o singură instrucțiune asupra aceleiași variabile sunt apelate de mai multe ori operațiile de increment și decrement, rezultatul poate fi nedefinit, iar comportamentul va depinde în mare măsură de compilatorul concret. De exemplu:
int count {1};
int result = ++count * 3 + count++ * 5;
Astfel, atât g++, cât și clang++ vor compila acest cod, iar rezultatul pentru variabila result va fi, în principiu, cel așteptat – 16, însă compilatorul clang++ va genera și un avertisment.
Redefinirea ordinii operațiilor
Parantezele permit modificarea ordinii de evaluare a expresiilor. De exemplu:
#include <iostream>
int main()
{
int a {8};
int b {7};
int c {(a + 5) * ++b}; // c = 104
std::cout << "c = " << c << std::endl;
}
Deși operația de adunare are o prioritate mai mică, aceasta va fi executată mai întâi, și nu înmulțirea, deoarece adunarea este încadrată între paranteze.