Tyyppimuunnokset (cast) / bittikäsittely
Tyyppimuunnokset:
delphissä voi tehdä esim. näin:
type
LongRec = packed record
B0, B1, B2, B3 : Byte;
end;
procedure Test1;
var
L : Longint; // 32 bits
T0, T1, T2, T3 : Byte; // 4 tavua, 8 bit/tavu
begin
L := $cafeBabe; // ylin tavu (MSB) on ca (hex)
// alin tavu (LSB) = be (hex)
T0 := LongRec(L).B0;
T1 := LongRec(L).B1;
T2 := LongRec(L).B2;
T3 := LongRec(L).B3;
// nyt myös T0 = be (hex) ja T3 = ca (hex)
end;
Miten vastaavan voisi tehdä javalla ?
Siis esim:
class TestiLuokka {
void Test1 () {
byte T0;
int L = 0xcafebabe; // ylin tavu (MSB) on ca (hex)
// Miten nyt T0:aan L:n alin tavu ?
// T3:een ylin tavu jne.
// onko tämä ainoa (hidas) vaihtoehto ???
T0 = (L & 255); // T0 = be (hex)
T1 = (L >>> 8) & 255;
T2 = (L >>> 16) & 255;
T3 = (L >>> 24) & 255; // T3 = ca
}
}
1. Toimiiko esittämäni javakoodi vastaavalla tavalla kuin edelläoleva delphikoodi ?
2. Aiheutuuko ongelmia siitä, että siinä missä Delphin BYTE on etumerkitön, eli arvoalueena 0..255, niin javassa byte onkin etumerkillinen, eli arvoalueena onkin -128 .. 127 (kuten Delphin ShortInt).
Kun nyt kaikki 4 esimerkissäni ollutta tavua ovat > 127, aiheutuuko tuosta ongelmia muunnoksissa (heittääkö poikkeuksia ?)
3. Onko nopeampia tapoja tehdä em.asia ?
Delphissähän tietueeseen perustuva typecastaus kääntyy suoraan muistiosoitukseksi jolloin mitään shift- tai and -operaatioita ei tarvita.
a) suoraan, kuten Delphissä
ja
b) voiko esim. edellä tuon x & 255 jättää pois,vai aiheuttaako esim tämä sijoitusyritys:
T0 = L; poikkeuksen, kun T0 on byte -tyyppiä ja L onkin int, jonka arvona on 0xcafebabe ? Tässähän yritetään (ilman tuota x & 255 -operaatiota) sijoittaa 32- bittistä arvoa 8 -bittiseen muuttujaan ?
4. Javassa ilmeisesti ainoa virallisesti etumerkitön tyyppi on 16 -bittinen char, joka on tarkoitettu unicode -merkkien käsittelyyn. Voiko kuitenkaan tehdä näin:
{
char u16;
u16 = 65535;
}
Silloin, kun tarvitaan 32 -bittisiä "bittisäiliöitä" binäärisen datan käsittelyyn, niin voiko int -tyyppiä käyttää tähän ja olla välittämättä siitä, että int on tosiasiassa etumerkillinen tyyppi, vai pitääkö homma hoitaa 64- bittisellä long -tyypillä, jolloin puolet sen kapasitetista jää käyttämättä ?
Entä merkkijonojen (String tai Stringbuffer) käyttö bittisäiliöinä, voiko javan merkkijonon merkki olla mikä hyvänsä (siis binääristä dataa, ja 16- bittisenä siis väliltä 0..65535) ?
Tyyppimuunnokset:
delphissä voi tehdä esim. näin:
type
LongRec = packed record
B0, B1, B2, B3 : Byte;
end;
procedure Test1;
var
L : Longint; // 32 bits
T0, T1, T2, T3 : Byte; // 4 tavua, 8 bit/tavu
begin
L := $cafeBabe; // ylin tavu (MSB) on ca (hex)
// alin tavu (LSB) = be (hex)
T0 := LongRec(L).B0;
T1 := LongRec(L).B1;
T2 := LongRec(L).B2;
T3 := LongRec(L).B3;
// nyt myös T0 = be (hex) ja T3 = ca (hex)
end;
Miten vastaavan voisi tehdä javalla ?
Siis esim:
class TestiLuokka {
void Test1 () {
byte T0;
int L = 0xcafebabe; // ylin tavu (MSB) on ca (hex)
// Miten nyt T0:aan L:n alin tavu ?
// T3:een ylin tavu jne.
// onko tämä ainoa (hidas) vaihtoehto ???
T0 = (L & 255); // T0 = be (hex)
T1 = (L >>> 8) & 255;
T2 = (L >>> 16) & 255;
T3 = (L >>> 24) & 255; // T3 = ca
}
}
1. Toimiiko esittämäni javakoodi vastaavalla tavalla kuin edelläoleva delphikoodi ?
2. Aiheutuuko ongelmia siitä, että siinä missä Delphin BYTE on etumerkitön, eli arvoalueena 0..255, niin javassa byte onkin etumerkillinen, eli arvoalueena onkin -128 .. 127 (kuten Delphin ShortInt).
Kun nyt kaikki 4 esimerkissäni ollutta tavua ovat > 127, aiheutuuko tuosta ongelmia muunnoksissa (heittääkö poikkeuksia ?)
3. Onko nopeampia tapoja tehdä em.asia ?
Delphissähän tietueeseen perustuva typecastaus kääntyy suoraan muistiosoitukseksi jolloin mitään shift- tai and -operaatioita ei tarvita.
a) suoraan, kuten Delphissä
ja
b) voiko esim. edellä tuon x & 255 jättää pois,vai aiheuttaako esim tämä sijoitusyritys:
T0 = L; poikkeuksen, kun T0 on byte -tyyppiä ja L onkin int, jonka arvona on 0xcafebabe ? Tässähän yritetään (ilman tuota x & 255 -operaatiota) sijoittaa 32- bittistä arvoa 8 -bittiseen muuttujaan ?
4. Javassa ilmeisesti ainoa virallisesti etumerkitön tyyppi on 16 -bittinen char, joka on tarkoitettu unicode -merkkien käsittelyyn. Voiko kuitenkaan tehdä näin:
{
char u16;
u16 = 65535;
}
Silloin, kun tarvitaan 32 -bittisiä "bittisäiliöitä" binäärisen datan käsittelyyn, niin voiko int -tyyppiä käyttää tähän ja olla välittämättä siitä, että int on tosiasiassa etumerkillinen tyyppi, vai pitääkö homma hoitaa 64- bittisellä long -tyypillä, jolloin puolet sen kapasitetista jää käyttämättä ?
Entä merkkijonojen (String tai Stringbuffer) käyttö bittisäiliöinä, voiko javan merkkijonon merkki olla mikä hyvänsä (siis binääristä dataa, ja 16- bittisenä siis väliltä 0..65535) ?
Vastaukset 4
Vanhimmat
1. Vastaus
Java koodi toimii siten kuin delphi koodi. Koodissa pitää kuitenkin allaolevaan tapaan kertoa, että tehdään tyyppi muunnos aivan kuten C:ssä (tai muuten kääntä antaa virheen):
T0 = (byte) (L & 255);
2. Vastaus
Riippuu miten käsittelet tavua. Esimerkiksi voit kirjoittaa lauseen "if (T0 < 200) ...", mutta Javassa se on aina tosi. Lukualueen rajojen ylitykset eivät luonnollisesti aiheuta poikkeuksia (eli voit kirjoittaa ilman mitään herjaa vaikka lauseen "T0 = 1000000").
3. Vastaus
Ei
3a. Vastaus
Ei
3b. Vastaus
Kyllä, kun muunnos tehdään noin päin, niin and operaatio voidaan jättää pois, (sillä pakotettu tyyppimuunnos tavallaan suorittaa sen jo, (eli vastauksessa 1 olevan esimerkin and operaatio on turha)). Toisinpäin tehtäessä and operaatio taas pitää olla, (eli silloin kun muunnat etumerkittömän tavun takaisin intiksi). Koska ilman and operaatiota etumerkki kopioituu, eli tavu 0xff (-1) muuttuukin muunnoksessa muotoon 0xffffffff (-1). Eli palautus funktio tuolle esimerkillesi olisi:
L = (T0 & 0xFF) | ((T1 & 0xFF)
Mainittakoon nyt vielä, että myös valmisluokkaa java.nio.ByteBuffer voi käyttää, jos haluaa jotain tavumassaa käsitellä. Siitä löytyy esim. getInt/putInt-metodit, joilla voi käsitellä neljää vierekkäistä tavua intteegerinä.
(Tosin jos haluaa vain yhdestä integeristä tavut irti, nopeusmielessä on varmaan paras vain tehdä se käsin shiftaamalla sopivasti. Ainakin gcj natiivikoodia kääntäessään näyttäisi olevan sitäpaitsi riittävän älykäs kääntämään esim. (byte)(i >> 16) yhden tavun lukemiseksi jos se on järkevää.)
char VOI käyttää kokonaislukuna ?!
En tiedä, kuinka yleispätevä havaintoni on, mutta seuraava esimerkkikoodi EI aiheuta poikkeuksia, ja palauttaa true !!!
Tämän perusteella näyttäisi siltä, että char -muuttujaan ja ennenkaikkea StringBuffer -luokan alkioihin (merkkeihin) VOI tallettaa mielivaltaisia kokonaislukuja väliltä 0-65535 ja ne tulevat muuttumattomina ulos.
Mutta se, että homma toimii Sunin omalla puhelinemulaattorilla: takaako se, että homma toimisi oikein muillakin laitteilla ?
----------------
static public boolean myTestA2() {
//
// static
final int maxVal = 65535;
boolean Ok;
StringBuffer St1 = new StringBuffer(65540);
int i;
// 1. initialize St1 with #0 .. #255
St1.setLength(0);
for(i=0; i
No, speksissä 'char' on listattu samassa joukossa kuin muutkin kokonaislukutyypit (kts. http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.2.1 esim.), joten noin maallikkona olettaisi, että se myös toimii sellaisena. En kylläkään näe suurta etua myös kaksitavuiseen "short"-tyyppiin verrattuna.
(String- ja StringBuffer-luokista on vaikeampi olla varma, niissä kun kyse on merkkijonoksi tulkituista asioista. Luulisi char-tyyppisiä arvoja palauttavien metodien toimivan; codePointAt:n ja muiden kanssa tulee ongelmia surrogaattiparien kanssa. java.nio.CharBuffer/ShortBuffer olisivat soveltuvampia, mutta näitä ei tietysti J2ME:stä löydy. Yksinkertaiseen käyttöön ei short[]-tyyppiseen taulukkoon perustuvan luokan kirjoittelu varmaan kauheasti rasita.)