De ce contează?
Imaginează-ți că numeri boabe de orez. Câteva încap în pumn, dar un milion? Nu. Atunci le pui în saci numerotați: sacul 0 ține unitățile, sacul 1 zecile, sacul 2 sutele și așa mai departe. Niciun sac singur nu trebuie să fie uriaș — important e să ai destui saci. Exact așa ținem un număr cu sute de cifre: nu într-o singură variabilă, ci într-un șir de „saci”, câte o cifră în fiecare.
De ce nu încap în long long
În C++, tipurile native au o margine fixă, dată de numărul de biți:
intare 32 de biți → ține numere până la circa 2 × 10⁹ (10 cifre);long longare 64 de biți → ține până la circa 9,2 × 10¹⁸, adică maximum 19 cifre.
Asta e suficient pentru multe probleme, dar nu pentru toate. La concurs apar des
cerințe gen „calculează 100!” (factorialul lui 100, care are 158 de cifre) sau
„al 200-lea număr Fibonacci”. Astfel de valori depășesc cu mult orice tip nativ.
Ce se întâmplă când depășești? Nu primești eroare — primești un rezultat fals. Valoarea „se învârte” (overflow): aduni două numere mari pozitive și obții brusc un număr negativ sau o cifră aiurea. Programul rulează liniștit și îți dă un răspuns greșit, ceea ce e și mai periculos decât o eroare vizibilă.
Concluzia: dacă numărul poate depăși 18–19 cifre, nu îl mai poți ține într-o singură variabilă. Avem nevoie de o altă reprezentare.
Reprezentarea: un vector de cifre, inversat
Ideea fundamentală a întregului capitol: stocăm un număr mare ca un vector<int>
în care fiecare element este o singură cifră zecimală (bază 10, pentru claritate
didactică). Convenția pe care o fixăm aici și o vom folosi identic în toate lecțiile:
Cifra unităților stă pe indexul 0. Adică ținem cifrele inversat (little-endian):
cifre[0]= unitățile,cifre[1]= zecile,cifre[2]= sutele.
De ce inversat, nu în ordinea „normală”? Pentru că la adunare, scădere și înmulțire transportul („carry-ul”) pleacă mereu de la unități și curge spre cifrele tot mai mari. Dacă unitățile sunt pe indexul 0, transportul curge dinspre index mic spre index mare — adică în direcția firească de parcurgere a vectorului. Astfel buclele viitoare devin curate și fără surprize. Dacă am ține cifrele în ordinea normală, ar trebui să parcurgem mereu de la coadă spre cap, complicând inutil codul.
Exemplu pas cu pas
Luăm numărul 4071 și îl punem în vector după convenția noastră:
- cifra unităților este 1 →
cifre[0] = 1 - cifra zecilor este 7 →
cifre[1] = 7 - cifra sutelor este 0 →
cifre[2] = 0 - cifra miilor este 4 →
cifre[3] = 4
Deci cifre = [1, 7, 0, 4]. Vectorul „arată” inversat față de cum scriem numărul,
dar asta e exact ce vrem. Ca să afișăm numărul înapoi, parcurgem de la indexul cel
mai mare spre 0: 4, 0, 7, 1 → „4071”. Numărul de cifre este cifre.size().
Implementare C++
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
string s = "4071"; // citim numarul ca text
vector<int> cifre; // aici stocam cifrele, inversat
// parcurgem string-ul de la coada spre cap:
// ultimul caracter al string-ului = cifra unitatilor = index 0
for (int i = s.size() - 1; i >= 0; i--) {
cifre.push_back(s[i] - '0'); // '7' - '0' = 7 (din char in int)
}
// acum cifre = [1, 7, 0, 4]
// afisare: de la indexul cel mai mare spre 0
for (int i = cifre.size() - 1; i >= 0; i--) {
cout << cifre[i]; // 4, apoi 0, apoi 7, apoi 1
}
cout << "\n"; // afiseaza: 4071
cout << cifre.size() << "\n"; // 4 (numarul are 4 cifre)
return 0;
}Convenția se numește little-endian („capătul mic la început”): cifra cea mai puțin semnificativă, unitatea, stă prima — pe indexul 0. E exact pe dos față de cum scriem și citim un număr pe hârtie, dar e firesc pentru calculator, fiindcă orice operație aritmetică pornește de la unități.
- Uiți să inversezi: pui
cifre[0] = 4(prima cifră a textului). Atunci unitățile ajung pe ultimul index, iar transportul de la adunare va curge greșit. Citește string-ul de la coadă spre cap. - Afișezi de la indexul 0: dacă scrii cifrele în ordinea
cifre[0], cifre[1], ..., numărul iese inversat („1704” în loc de „4071”). Afișează mereu de lasize() - 1spre 0. - Zerouri nesemnificative în față: dacă rămân cifre 0 pe indecșii cei mai mari (ex.
[1, 7, 0, 4, 0, 0]), la afișare obții „004071”. Elimină zerourile din coada vectorului înainte de afișare. - Folosești
long longpentru ceva ce-l depășește: dacă numărul are peste 18–19 cifre, tipul nativ dă overflow tăcut. Folosește reprezentarea cu vector de cifre.
1 | 7 | 0 | 4 |
0 | 1 | 2 | 3 |
Recapitulare
- Tipurile native au limită fixă:
long longține maximum ~19 cifre, apoi dă overflow tăcut. - Stocăm un număr mare ca
vector<int>cu o cifră per element, inversat: unitățile pe indexul 0. - Citim numărul ca
stringși îl punem în vector de la coadă spre cap; afișăm de lasize() - 1spre 0.