Il Java penso sia il linguaggio che più odio. Lo trovo un affarone sconclusionato che unisce la complessità di un linguaggio compilato alla pesantezza di un linguaggio di scripting.
Dopo questa premessa che sa molto di sfogo possiamo arrivare al dunque. Mi è capitato in questi giorni di avere bisogno di gestire dei singoli byte. Il Java ha il tipo byte al suo interno ma li gestisce con il segno, il che è abbastanza stupido dato che di solito uno che lavora con i byte li vuole unsigned. Dire che i byte sono interpretati come signed significa che degli 8bit che costituiscono un byte ne vengono usati 7 per rappresentare il numero e 1 per il segno. Questo causa fondamentalmente due problemi:
- Il massimo numero positivo rappresentabile non è più 255 ma 127.
- Bisogna stare attenti al segno. Ad esempio non si può fare un ciclo da 0 a 255 che scandisca tutti i possibili byte ma bisogna farlo da -128 a 127. Insomma, una rottura.
In tutti gli altri linguaggi esiste la parola chiave unsigned che permette con facilità di passare da interpretazioni con segno a interpretazioni senza segno. Nel Java NO. In Java tutti i tipi di dato sono sempre considerati signed ad eccezione del tipo char.
Abbiamo quindi bisogno di un modo per utilizzare dati unsigned.
La soluzione consiste nel promuovere il tipo di dato a quello più grande e passarlo in una maschera. Un esempio chiarirà il concetto:
Passare da byte signed a unsigned:
public static int unsignedByte(byte b) { int result = b & 0xFF; return result; }
Come vedete basta inserire il byte in un int e fare un and bit-a-bit per annullare tutto all’infuori del byte.
Questo può sembrarvi uno spreco di memoria in quanto memorizzate 1 byte in un int (4 byte). Invace no. In Java, per questioni prestazionali, il tipo byte occupa comunque 4 byte. Quindi lo spazio utilizzato è sempre lo stesso.
Analogamente potete creare un unsigned int inserendo un int in un long e mettendo in and con 0xFFFFFFFF. Questa volta però un po di spazio lo buttate.
Passare da int signed a unsigned:
public static long unsignedInt(int b) { long result = b & 0xFFFFFFFF; return result; }
E’ bene tenere presente però che per molte operazioni l’uso di byte signed o unsigned è del tutto equivalente come ad esempio l’operazione di divisione intera o di modulo.
Ora non vi resta che divertirvi a giocare con i segni nella speranza che vi divertiate più di quanto mi sia divertito io a trovare la soluzione a questo problema.