Tämä sivu perustuu Arto Wiklan ohjelmointisivustoon. Materiaalin copyright © Arto Wikla. Materiaalia saa vapaasti käyttää itseopiskeluun. Muu käyttö vaatii luvan.

Javan kokonais- ja liukulukutyyppejä, tyyppimuunnoksia

(Muutettu viimeksi 7.9.2010, sivu perustettu 7.9.2010.)

Kokonaislukutyypit

Kokonaislukuarvot (paitsi char) esitetään tietokoneessa ns. 2-komplementtimuodossa. Laskutoimitusten mahdollisesti aiheuttamaa ns. ylivuotoa tai alivuotoa ei havaita!

Literaalivakiot kirjoitetaan tuttuun tapaan: 1, -54, 0, ...

[Ns. oktaali- ja heksadesimaaliarvoille on oma esitysmuotonsa. On paras olla käyttämättä 'tarpeettomia' etunollia, jos ei erityisesti pidä oktaaliluvuista! Myös long-vakioille on oma esitysmuotonsa: 34L]

Erityyppisten kokonaislukuarvojen sekoittaminen ei aina ole ongelmatonta. Luvussa 3.3 opitaan miten kokonaislukutyyppien arvoja voi muuttaa toisikseen.

Merkkityyppi char

Merkkiarvot (char) ovat positiivisia kahden tavun mittaisia kokonaislukuja. Merkit on koodattu Unicode-koodilla, joka sisältää maailman useimpien kirjoitusjärjestelmien kirjain- ja numeromerkit. Esitettävissä ovat esimerkiksi mm. seuraavien kielten merkistö: kreikka, kopti, venäjä, heprea, arabia, devanagari, bengali, gurmukhi, gujarati, tamili, telegu, tai, lao, hiragana, katakana, ...

Merkkivakiot esitetään yksinkertaisissa lainausmerkeissä, esimerkiksi: 'A', 'k', ' ', ';', '#', ... . (Kaksoislainausmerkit on varattu String-tyyppisille vakioille!)

Eräille erikoismerkeille on erityiset ilmaukset:

          '\b'     backspace 
          '\t'     tabulaattori (tab)
          '\n'     rivinvaihto (lf)
          '\f'     sivunvaihto (ff)
          '\r'     telanpalautus (cr)
          '\"'     kaksinkertainen lainausmerkki
          '\''     yksinkertainen lainausmerkki
          '\\'     takakenoviiva
          '\ddd'   merkki, jonka koodi oktaalilukuna on ddd
          '\udddd' Unicode-merkki, jonka koodi hekdadesimaali-
                   lukuna on dddd 

Huom: Näitä ilmauksia voi käyttää myös String-vakioissa!
Huom: '\u0000' on ns. null-merkki. Jos se esimerkiksi tulostetaan, mitään ei näy...

Liukulukutyypit

Javan liukulukutyypit ovat:

Liukulukuliteraalit ovat muotoa: 3.14, 124., .12, 5.6e3, 0.3E-9, 3e-10, 1.8e+3, ...

Huom: Kaikki liukulukuliteraalit ovat oletusarvoisesti tyyppiä double! Vakion saa float-tyyppiseksi kirjoittamalla sen loppuun kirjaimen f tai F: 3.14f, 124.F, .12F, 5.6e3f, 0.3E-9f, ...

(Javan liukulukuaritmetiikka (lähes!) noudattaa standardia IEEE 754.)

(Liukulukujen arvoalueeseen kuuluvat myös positiivinen ja negatiivinen ääretön ja erityinen NaN-arvo, "not a number". Näiden avulla pidetään mm. huoli siitä, että alilausekkeen laskennassa tullut "huono" arvo tekee koko lausekkeen arvon "huonoksi". Tällä kurssilla ei numeerisen laskennan periaatteita käsitellä!)

Tyypit ja tyyppimuunnokset

Java on ns. vahvasti tyypitetty kieli: lausekkeen tyyppi tiedetään (lähes aina) jo käännösaikana, ns. automaattisia tyyppimuunnoksia (implicit conversion) ei tehdä kuin turvallisissa tapauksissa. Tämä tarkoittaa sitä, ettei kääntäjä salli esimerkiksi double-arvon sijoittamista int-muuttujalle, eikä edes double-vakion "0.0" sijoittamista float-muuttujalle vaikka arvo sinne mahtuisikin! (Tarkempia tietoja löytyy Javan spesifikaatiosta: The Java Language Specification: 5.2 Assignment Conversion)

Sääntöjä:

Esimerkki (nimetään muuttujat vähän erikoisesti alkamaan merkillä "_"):

   short  _short  = -23;
   int    _int    = 123;
   long   _long   = 121234;
   char   _char   = 'q';
   float  _float  = 3.14f;
   double _double = 121.2311;

// sallittuja             kiellettyjä
   _int    = _short;     _short = _int;
   _long   = _int;       _int   = _long;
   _double = _short;     _short = _double;
   _double = _float;     _float = _double;

   _int = _char;         _char  = _int;
                         _short = _char;
                         _char  = _short;
   _double = _char;
                         _short = 8-0;

                                 // kokonaislukulaskutoimitukset tehdään
                                 // aina vähintään int-tyyppisinä!!

Kääntäjä antaa seuraavantapaisia ilmoituksia (4. virheellinen tapaus):

Ohjelma.java:12: possible loss of precision
found   : double
required: float
   _float = _double;
            ^
1 error

Eksplisiittinen tyyppimuunnos (cast) ilmaistaan kirjoittamalla arvon saajan tyyppinimi sulkeisiin lausekkeen eteen:

   short  _short  = -23;
   int    _int    = 123;
   long   _long   = 121234;
   char   _char   = 'q';
   float  _float  = 3.14f;
   double _double = 121.2311;

   _short = (short)_int;
   _int   = (int)_long;
   _short = (short)_double;
   _float = (float)_double;
   _char  = (char)_int;
   _short = (short)_char;
   _char  = (char)_short;
   _short = (short)(8-0);

   _short = (short)(3.14 * 1000.3 - 0.00002);
                   // huom. sulkeet lausekkeen ympärillä!!

Järkevillä tapauksilla on järkevä tulkinta: liukuluku katkaistaan kokonaisluvuksi. Kun double-arvosta tehdään float, esitystarkkuus voi pienetä; sopivankokoinen kokonaisluku voidaan muuttaa char-arvoksi, ... (Tarkemmin asiaa esitellään spesifikaatiossa: 5.1.3 Narrowing Primitive Conversions)

Järjettömissä tapauksissa sattuu järjettömyyksiä, joista ohjelmoija kantaa vastuun!

Huom: boolean-tyyppistä lauseketta ei voi muuntaa numeeriseksi edes eksplisiittisellä tyyppimuunnoksella!

Aritmeettiset operaatiot ja tyypit

Javassa kokonaislukulaskutoimitusten ns. yli- ja alivuotoja ei havaita! Kokonaisluvut toimivat 'modulolaskureiden' tapaan: suurimman arvon seuraaja on pienin arvo! Liukulukutyyppejä on laajennettu arvoilla "positiivinen ääretön", "negatiivinen ääretön" ja "ei luku", ... Myös erimerkkiset nollat ovat esitettävissä. Mutta kuten jo aiemmin todettiin, numeerisen laskennan hienouksia ei tällä kurssilla käsitellä.

Kaikille numeerisille tyypeille on käytössä operaatiot:

   +  -  *  /  %

Jos molemmat operandit (so. laskettavat) ovat kokonaislukutyyppisiä, myös laskutoimituksen tulos on on kokonaisluku (aina vain joko int tai long - operandien tyypistä riippuen; jos arvo sijoitetaan byte- tai short-muuttujaan, on tehtävä eksplisiittinen tyyppimuunnos!).

Operaatio "/" tarkoittaa kokonaisjakoa, jos sekä osoittaja että nimittäjä ovat jotain kokonaislukutyyppiä. Aina muulloin kyseessä on liukulukujakolasku.

Operaatio "%" on jäännösjako. Tuloksen etumerkki on sama kuin osoittajan etumerkki, nimittäjän etumerkillä ei ole vaikutusta tuloksen etumerkkiin. Jäännösjaon soveltaminen liukuluvuille on sallittua, mutta ei järkevää! (Ks. spesifikaatio 15.17.3 Remainder Operator %.)

Huom: Jos kahden kokonaislukuarvon jakamisen tuloksena halutaan liukuluku, on se erikseen ilmaistava. Esimerkiksi ohjelma:

public class Aritmetiikkaa {
   public static void main(String[] args) {
      int a = 5, b = 2;

      System.out.println( a/b );
      System.out.println( a/b * 1.0 );
      System.out.println( 1.0 * a/b );
      System.out.println( (float)a/b );
      System.out.println( (float)(a/b) );
      System.out.println( (a/(float)b) );
   }
}

tulostaa

2
2.0
2.5
2.5
2.0
2.5

Huom: Operaatio "+" tarkoittaa merkkijonojen katenointia (l. yhteenliittämistä) jos edes toinen 'yhteenlaskettavista' on String-olio (laskenta etenee vasemmalta oikealle). Esimerkki:

   int i = 2, j = 5;

   System.out.println( "Summa on " + i+j );
   // tulostaa: Summa on 25

   System.out.println( "Summa on " + (i+j) );
   // tulostaa: Summa on 7

   System.out.println( i+j + " on summa" );
   // tulostaa: 7 on summa

   System.out.println( i+ (j + " on summa") );
   // tulostaa: 25 on summa

Matemaattisia perusfunktioita

Luokassa Math on määritelty joukko numeeristen lausekkeiden laskennassa käytettäviä perusfunktioita ja pari vakiota (luokka Math kuuluu pakkaukseen java.lang, joka on ohjelmoijan käytettävissä ilman eri määrittelyitä).

Vakioita:

Math.E   // luku e   (2.718281828459045)
Math.PI  // luku pii (3.141592653589793)

Funktioita (monia muitakin on!):

Math.abs      // itseisarvo

Math.sin
Math.cos
Math.tan  

Math.exp  
Math.log      // luonnollinen logaritmi

Math.sqrt
Math.pow      // 1. double potenssiin 2. double

Math.round

Math.random() // double-pseudosatunnaisluku väliltä [0.0, 1.0)

Math.max
Math.min

Monet Math-luokan metodit on kuormitettu eri tyyppisille parametreille. API-kuvauksessa on täydellinen luettelo Math-luokan metodeista.

Esimerkki:

  for (double d=0.0; d < 2*Math.PI; d+=0.1)
    System.out.println(Math.sin(d));