2010-10-02 20 views
8

Così al lavoro ieri, ho dovuto scrivere un'applicazione per contare le pagine in un file AFP. Così ho rispolverato il mio MO: PDF di specifiche DCA e ho trovato il campo strutturato BPG (Begin Page) e il suo identificatore a 3 byte. L'app deve essere eseguita su una scatola AIX, quindi ho deciso di scriverla in Java.Informazioni sui byte Java

Per la massima efficienza, ho deciso di leggere i primi 6 byte di ogni campo strutturato e quindi di saltare i restanti byte nel campo. Questo otterrebbe me:

0: Start of field byte 
1-2: 2-byte length of field 
3-5: 3-byte sequence identifying the type of field 

Così ho controllare il tipo di campo e incrementare un contatore pagina se è BPG, e io non fare se non lo è. Quindi salterò i restanti byte nel campo invece di leggerli. E qui, nel salto (e davvero nella lunghezza del campo) è dove ho scoperto che Java utilizza i byte firmati.

Ho fatto qualche ricerca su google e ho trovato un bel po 'di informazioni utili. La maggior parte utile, naturalmente, era l'istruzione per fare un bit a bit & a 0xff per ottenere il valore int unsigned. Questo è stato necessario per ottenere una lunghezza che potrebbe essere utilizzata nel calcolo del numero di byte da saltare.

Ora so che a 128 inizieremo il conteggio indietro da -128. Quello che voglio sapere è come funziona l'operazione bitwise qui - in particolare, come arrivo alla rappresentazione binaria per un numero negativo.

Se capisco correttamente il bit a bit &, il risultato è uguale a un numero in cui sono impostati solo i bit comuni dei due numeri. Quindi, supponendo byte b = -128, avremmo:

b & 0xff // 128 

1000 0000-128 
1111 1111 255 
--------- 
1000 0000 128 

Così come sarei arrivato a 1000 0000 per -128? Come otterrei la rappresentazione binaria di qualcosa di meno ovvio come -72 o -64?

risposta

18

Al fine di ottenere la rappresentazione binaria di un numero negativo si calcola complemento a due:

  • Ottenere la rappresentazione binaria del numero positivo
  • Inverti tutti i bit
  • Aggiungi un

Facciamo -72 come esempio:

0100 1000 72 
1011 0111 All bits inverted 
1011 1000 Add one 

Quindi la rappresentazione binaria (8 bit) di -72 è 10111000.

Quello che sta effettivamente accadendo a voi è il seguente: Il file ha un byte con valore 10111000. Interpretato come un byte senza segno (che è probabilmente quello che si desidera), questo è 88.

In Java, quando questo byte viene utilizzato come int (ad esempio perché read() restituisce un int, o a causa di promozione implicita), verrà interpretato come un byte con segno e esteso per il segno a 11111111 11111111 11111111 10111000. Questo è un intero con valore -72.

Con ANDing con 0xff si conservano solo i più bassi 8 bit, in modo che il numero intero è ora 00000000 00000000 00000000 10111000, che è 88.

+0

+1 per la menzione che l'operazione si verifica in un interno con estensione di segno. –

+4

Questo è esattamente quello che cercavo, grazie mille.Questo è il motivo per cui adoro Stackoverflow. –

0

Per ottenere il valore di byte senza segno è possibile.

int u = b & 0xFF; 

o

int u = b < 0 ? b + 256 : b; 
2

Quello che voglio sapere è come dell'operazione bit a bit lavora qui - più specificamente, come arrivo alla rappresentazione binaria di un numero negativo.

La rappresentazione binaria di un numero negativo è quella del numero positivo corrispondente bit-capovolto con 1 aggiunto ad esso. Questa rappresentazione è denominata two's complement.

1

Immagino che la magia qui sia che il byte è memorizzato in un contenitore più grande, probabilmente un int di 32 bit. E se il byte è interpretato come un byte con segno, viene espanso per rappresentare lo stesso numero nel int a 32 bit, cioè se il bit più significativo (il primo) del byte è un 1 allora nel 32 bit int all anche i bit rimasti di quel 1 sono girati a 1 (ciò è dovuto al modo in cui i numeri negativi sono rappresentati, il complemento a due).

Ora, se si è & 0xFF che si tagliano quelli 1 e si finisce con un "positivo" int che rappresenta il valore di byte che hai letto.

0

Per byte con il bit 7 set:

unsigned_value = signed_value + 256 

Matematicamente quando sei al computer con i byte calcolano il modulo 256. La differenza tra firmata e non firmata è che si scelgono rappresentanti diversi per le classi di equivalenza, mentre la rappresentazione sottostante come modello di bit rimane la stessa per ogni classe di equivalenza. Questo spiega anche perché l'addizione, la sottrazione e la moltiplicazione hanno lo stesso risultato di un pattern di bit, indipendentemente dal fatto che si calcolino con interi firmati o senza segno.

1

Non sai cosa vuoi veramente :) Presumo che tu stia chiedendo come estrarre un valore multi-byte firmato? In primo luogo, guardare a ciò che succede quando si firma estendere un singolo byte:

byte[] b = new byte[] { -128 }; 
int i = b[0]; 
System.out.println(i); // prints -128! 

Così, il segno è correttamente extendet a 32 bit senza fare nulla di speciale. Il byte 1000 0000 si estende correttamente a 1111 1111 1111 1111 1111 1111 1000 0000. Sai già come sopprimere l'estensione di segno con AND'ing con 0xFF - per i valori multi byte, vuoi solo il segno del byte più significativo da estendere e i byte meno significativi si vuole trattare come senza segno (esempio presuppone ordine di byte di rete, a 16 bit int value):

byte[] b = new byte[] { -128, 1 }; // 0x80, 0x01 
int i = (b[0] << 8) | (b[1] & 0xFF); 
System.out.println(i); // prints -32767! 
System.out.println(Integer.toHexString(i)); // prints ffff8001 

È necessario sopprimere l'estensione segno di ogni byte, tranne quello più significativo, in modo per estrarre un segno a 32 bit int ad un 64-bit lungo:

byte[] b = new byte[] { -54, -2, -70, -66 }; // 0xca, 0xfe, 0xba, 0xbe 
long l = (b[0]   << 24) | 
     ((b[1] & 0xFF) << 16) | 
     ((b[2] & 0xFF) << 8) | 
     ((b[3] & 0xFF)  ); 
System.out.println(l); // prints -889275714 
System.out.println(Long.toHexString(l)); // prints ffffffffcafebabe 

Nota: su sistemi Intel basati, byte sono oft it memorizzato in ordine inverso (prima il byte meno significativo) perché l'architettura x86 memorizza le entità più grandi in questo ordine in memoria. Molti software originali x86 lo usano anche in formati di file.