Tie koodariksi

C++-ohjelmointi

Luku 9: Lisää tyypeistä

Kokonaisluvut

C++:n kokonaislukutyypit ovat:
tyyppitavallinen kokotavallinen alarajatavallinen yläraja
char8 bittiä–27 = –12827–1 = 127
short16 bittiä–215 = –32768215–1 = 32767
int32 bittiä–231 ≈ –2 · 109231–1 ≈ 2 · 109
long64 bittiä–263 ≈ –9 · 1018263–1 ≈ 9 · 1018
long long64 bittiä–263 ≈ –9 · 1018263–1 ≈ 9 · 1018
C++-standardi ei määrittele tarkasti, kuinka suuria tyypit ovat, vaan tämä riippuu käytetystä ympäristöstä. Tässä taulukossa on mainittu tyypilliset koot nykyaikaisella tietokoneella.

Tavallinen valinta kokonaisluvun tyypiksi on int, joka riittää useimmissa tapauksissa. Jos tarvitaan suurempia lukuja, voi käyttää tyyppiä long (tai long long).

Koodissa oleva kokonaisluku on oletuksena int-tyyppinen, mutta jos luku ei mahdu int-tyyppiin, se saa laajemman tyypin. Esimerkiksi 123 on int-tyyppinen, mutta 123123123123 voi olla long-tyyppinen. Lisäksi tyyppiä voi tarkentaa loppuliitteellä: 123L on long-tyyppinen ja 123LL on long long -tyyppinen.

Liukuluvut

C++:n liukulukutyypit ovat:
tyyppitavallinen kokotavallinen alarajatavallinen yläraja
float32 bittiä≈ –3.4 · 1038≈ 3.4 · 1038
double64 bittiä≈ –1.8 · 10308≈ 1.8 · 10308
long double128 bittiä≈ –1.2 · 104932≈ 1.2 · 104932
Liukuluku tarkoittaa desimaalilukua. Kuten kokonaisluvuissa, C++-standardi ei määrittele tarkasti tyyppien kokoja, ja taulukossa on mainittu nykyään tyypilliset koot.

Tavallinen valinta liukuluvun tyypiksi on double. Seuraava koodi näyttää kaksi tapaa liukuluvun merkintään:

double a = 5.72;
double b = 2e5;
Ensimmäisessä tavassa luvussa 5.72 on desimaalipiste, jolloin se tulkitaan liukulukuna. Toinen tapa on käyttää e-merkintää: esimerkiksi 2e5 vastaa liukulukua 200000.

Liukulukujen avulla voi esittää hyvin pieniä ja suuria lukuja, mutta monia lukuja ei voi esittää tarkasti ja laskuissa voi tapahtua pyöristysvirheitä.

Tyypin koko

Tietyn tyypin koon omassa ympäristössä voi selvittää sizeof-sanan avulla, joka antaa koon tavuina. Esimerkiksi seuraava koodi tulostaa int-tyypin koon:
cout << sizeof(int) << "\n";
Seuraava tuloste vastaa 32-bittistä tyyppiä:
4

Uusi nimi tyypille

Sanan typedef avulla voi antaa uuden nimen tyypille. Esimerkiksi seuraava rivi antaa tyypille long long lyhyemmän vaihtoehtoisen nimen ll.
typedef long long ll;
Tämän jälkeen uutta tyypin nimeä voi käyttää näin:
ll x = 5;
Toinen uudempi tapa tehdä sama on käyttää using-sanaa seuraavasti:
using ll = long long;

Tyyppimuunnokset

C++ muuttaa eri tilanteissa automaattisesti tyyppiä. Jos operaattorin eri puolilla on eri tyyppi, käytetään laajempaa tyyppiä. Seuraava koodi havainnollistaa asiaa:
cout << 1/2 << "\n"; // 0
cout << 1.0/2 << "\n"; // 0.5
cout << 1/2.0 << "\n"; // 0.5
cout << 1.0/2.0 << "\n"; // 0.5
Ensimmäisessä laskussa molemmat luvut ovat kokonaislukuja, joten lasku tapahtuu kokonaisluvuilla. Muissa laskuissa ainakin toinen luku on liukuluku, joten lasku tapahtuu liukuluvuilla.

Tyyppiä voi myös muuttaa laittamalla halutun tyypin sulkujen kanssa muutettavan arvon eteen. Sulut voi laittaa joko muutettavan arvon tai tyypin ympärille:

cout << double(1)/2 << "\n"; // 0.5
cout << (double)1/2 << "\n"; // 0.5

Rajojen ylitys

Jos kokonaisluvun suuruus ylittää suurimman mahdollisen luvun tai alittaa pienimmän mahdollisen luvun, luku pyörähtää ympäri vastakkaiselle puolelle. Esimerkiksi seuraavassa koodissa muuttujassa on suurin mahdollinen int-arvo. Kun tähän lisätään yksi, muuttujaan ilmestyy pienin mahdollinen int-arvo.
int x = 2147483647;
x++;
cout << x << "\n"; // -2147483648 
Liukuluvuissa käytössä ovat merkinnät inf ja -inf, jotka tarkoittavat positiivista ja negatiivista ääretöntä (lyhenne sanasta "infinity"). Jos luvusta tulee liian suuri tai pieni, se muuttuu tällaiseksi. Esimerkiksi seuraavassa koodissa muuttujan arvo ylittää double-tyypin rajan, joten siitä tulee inf:
double x = 1e308;
x *= 2;
cout << x << "\n"; // inf

Olemattomat luvut

Tarkastellaan seuraavaa koodia:
int a, b;
cin >> a >> b;
cout << a/b << "\n";
Jos b on 0, ohjelma päättyy virheeseen, koska nollalla jakaminen on mahdotonta:
Floating point exception (core dumped)
Tarkastellaan sitten vastaavaa koodia liukuluvuilla:
double a, b;
cin >> a >> b;
cout << a/b << "\n";
Nyt jos b on 0, ohjelma ei pääty virheeseen, vaan laskun tulokseksi tulee erikoisarvo. Jos a on positiivinen, tulos on inf, ja jos a on negatiivinen, tulos on -inf. Jos taas a on 0, tulos onkin -nan, mikä on lyhenne sanoista "not a number", eli kyseessä ei ole luku (eikä ääretön).

Liukulukujen tarkkuus

Liukulukujen heikkoutena on, että monia lukuja ei voi esittää tarkasti liukulukuna ja tapahtuu pyöristysvirheitä. Tarkastellaan esimerkkinä seuraavaa koodia:
double a = 0.3*3+0.1;
double b = 1;
cout << a << " " << b << "\n";
if (a == b) cout << "luvut ovat samat\n";
if (a < b) cout << "a on pienempi\n";
if (a > b) cout << "a on suurempi\n";
Koodi tuottaa yllättävän tuloksen:
1 1
a on pienempi
Vaikka näyttää siltä, että molemmissa muuttujissa on luku 1, muuttuja a on hieman pienempi. Syynä on, että laskua 0.3*3+0.1 ei pystytä laskemaan tarkasti vaan tulee pyöristysvirhe.

Liukulukuja käyttäessä tuleekin varautua siihen, että tulokset eivät ole tarkkoja. Jos ei ole hyvää syytä käyttää liukulukuja, kannattaa mieluummin käyttää kokonaislukuja.