2011-04-28 11 views
14

Ho un breve, instr, che assomiglia a questo:Segno estendere un numero nove bit in C

1110xxx111111111 

ho bisogno di tirare fuori i bit 0-9, cosa che faccio con (instr & 0x1FF). Questa quantità viene quindi memorizzata in un nuovo corto. Il problema è che quando ciò si verifica, diventa 0x0000000111111111, non 0x1111111111111111 come voglio. Come posso risolvere questo? Grazie!

EDIT

Ecco il codice:

short instr = state->mem[state->pc]; 
unsigned int reg = instr >> 9 & 7; // 0b111 
state->regs[reg] = state->pc + (instr & 0x1FF); 

Questo è un simulatore che legge in assemblea. state è la macchina, regs[] sono i registri e pc è l'indirizzo dell'istruzione corrente in mem[].

Questo va bene se gli ultimi nove bit rappresentano un numero positivo, ma se rappresentano -1, viene memorizzato come tutti gli 1, che è interpretato come un valore positivo dal mio codice.

+1

Non sono sicuro ho capito la domanda o il processo che si sta utilizzando. Ti interessa condividere il codice che stai utilizzando per ottenere questi risultati? – joce

+0

Questo non è chiaro, chi dovrebbe diventare '0x1111111111111111'? – MByD

risposta

19

È possibile farlo manualmente: (instr & 0x1FF) | ((instr & 0x100) ? 0xFE00 : 0). Verifica il bit di segno (il bit più in alto che stai mantenendo, 0x100) e imposta tutti i bit sopra di esso se il bit di segno è impostato. È possibile estendere questo valore a 5 bit adattando le maschere a 0x1F, 0x10 e 0xFFE0, essendo i 5 bit inferiori, il quinto bit stesso e tutti i bit 5-16 rispettivamente.

Oppure si può trovare qualche scusa per assegnare i bit alla parte superiore di un corto firmato e spostarli verso il basso (ottenendo un'estensione di segno nel processo): short x = (instr & 0x1FF) << 7; x >>= 7; Quest'ultimo potrebbe effettivamente risultare più semplice in assembly e non coinvolgerà un ramo. Se è firmato, è possibile farlo in una singola espressione: (instr & 0x1FF) <<7>> 7. Dal momento che già rimuove i bit superiori semplifica a instr <<7>> 7. Sostituire 7 con 11 per 5 bit (16-5).

+0

Come potrebbe essere adattato per un numero a 5 bit? Devo gestire entrambi i tipi in questo programma. –

+3

Su quest'ultimo, non potresti semplicemente fare '(instr & 0x1FF << 7) >> 7'? –

+1

Ho aggiornato la risposta per rispondere ai commenti ... –

2

Non sono sicuro di come si ottengono 13 1 bit dopo il mascheramento con 0x1ff, ma questo dovrebbe sign-estendere un numero di 9 bit in un breve di 16 bit. Non abbastanza (o particolarmente efficiente), ma funziona:

 
(instr & 0x1ff) | (0xfe00 * ((instr & 0x100) >> 8)) 

mascherare il bit di segno, spostamento a 1 grado di ottenere 0/1. Moltiplicare per i bit superiori, se il segno è 1, quindi il numero a 9 bit sarà OR con 0xfe, che imposterà tutti i bit superiori a 1.

+0

Non ho ancora provato questo, ma come potrebbe essere adattato per un numero a 5 bit? Ho bisogno di ottenere entrambi i tipi in questo programma. E hai ragione riguardo ai risultati; Ho lavorato per ore, quindi sono esausto. : D –

+0

Mascherare il bit del segno e spostare quel bit nella posizione 1 (in modo da ottenere 1 se si ha un numero negativo e 0 altrimenti). Quindi creare una maschera OR per i bit estesi e moltiplicare questo per il segno spostato. Se il tuo numero originale era negativo, questa maschera rimarrà tutte le altre, e in caso contrario saranno tutti zeri. O questo con il tuo numero mascherato per riempire i bit superiori con 1. –

4
(instr & 0x1FF) * (1 - ((unsigned short)(instr & 0x100) >> 7)) 

Come funziona? Seleziona il bit del tuo segno e lo sposta in posizione 2. Viene utilizzato per generare il valore 1 (se il bit del segno era assente) o -1 (se il bit del segno era presente).

Questa soluzione è senza diramazione e non dipende da un comportamento non definito.

+3

Su un microcontrollore sconosciuto probabilmente dovrei sbagliare sul lato della ramificazione piuttosto che moltiplicarlo. La penalità di ramo su tali CPU è piccola e la penalità se non c'è un moltiplicatore hardware (o forse anche se c'è!) È molto più alta. In effetti, dato il rischio di no * barrel shifter * forse il semplice ramo/test è il migliore. –

2

Appena imbattuto in questa ricerca di qualcos'altro, forse un po 'tardi, ma forse sarà utile per qualcun altro. AFAIAC tutti i programmatori C dovrebbero iniziare a programmare l'assemblatore.

In ogni caso, l'estensione del segno è molto più semplice rispetto alle altre 2 proposte. Assicurati di utilizzare le variabili con segno e quindi usa 2 turni.

short instr = state->mem[state->pc]; 
unsigned int reg = (instr >> 9) & 7; // 0b111 
instr &= 0x1ff; // get lower 9 bits 
instr = ((instr << 7) >> 7); // sign extend 
state->regs[reg] = state->pc + instr; 

Se la variabile è firmato quindi il compilatore C traduce >> all'aritmetica Sposta a destra che ha conservato il segno. Questo comportamento è indipendente dalla piattaforma.

Quindi, supponendo che l'istr abbia inizio con 0x1ff, allora abbiamo < < 7 SL (Shift Left) il valore in modo che instr sia ora 0xff80, quindi >> 7 ASR il valore in modo che instr sia ora 0xffff.

+0

È necessario "instr & = 0x1ff; // avere 9 bit più bassi" necessari in questo contesto? Questi pezzetti vengono comunque allontanati :) –

8

* No ramificazione richiesto *

Vedi http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend per un elenco di hack bit molto utili. Specificamente, firmare estende un numero è semplice come:

/* generate the sign bit mask. 'b' is the extracted number of bits */ 
int m = 1U << (b - 1); 

/* Transform a 'b' bits unsigned number 'x' into a signed number 'r' */ 
int r = (x^m) - m; 

Potrebbe essere necessario cancellare i bit superiori dei 'x' se non sono zero (x = x & ((1U << b) - 1);) prima di utilizzare la procedura sopra descritta.

Se il numero di bit "b" è noto al momento della compilazione (ad esempio, 5 bit nel tuo caso) c'è anche una soluzione più semplice (questo potrebbe attivare una specifica istruzione di estensione del segno se il processore lo supporta e il compilatore è abbastanza intelligente):

struct {signed int x:5;} s; 
r = s.x = x; 
1

Questo è più di un affinamento delle risposte precedenti, ma non completamente soluzione generica è stata presentata finora. Questa macro firmerà estendere un valore v con sb indicando il numero di bit basato su 0 del bit di segno.

#define SIGNEX(v, sb) ((v) | (((v) & (1 << (sb))) ? ~((1 << (sb))-1) : 0)) 

int32_t x; 

SIGNEX(x, 15); // Sign bit is bit-15 (16th from the right) 
SIGNEX(x, 23); // Sign bit is bit-23 (24th from the right) 

Esso utilizza ramificazione di massimizzare la portabilità su piattaforme che mancano di un moltiplicano hardware o barrel shifter.

0

una soluzione più facile è questa, per x essere un numero complemento 5-bit 2 s', guarda:

z = (x^16)-16 
Problemi correlati