Qualche giorno fa, il membro StackExchange Anto ha chiesto informazioni sugli usi validi per gli operatori bit-saggi. Ho affermato che lo spostamento era più veloce della moltiplicazione e della divisione di numeri interi per potenze di due. Il membro di StackExchange, Daemin, ha risposto affermando che lo spostamento a destra presentava problemi con numeri negativi.
A quel punto, non avevo mai pensato di usare gli operatori di turno con numeri interi con segno. Ho usato principalmente questa tecnica nello sviluppo di software di basso livello; pertanto, ho sempre usato numeri interi senza segno. C esegue spostamenti logici su numeri interi senza segno. Non viene prestata attenzione al bit di segno quando si esegue uno spostamento logico a destra. I bit liberati sono riempiti con zeri. Tuttavia, C esegue un'operazione di spostamento aritmetico quando si sposta a destra un intero con segno. I bit liberati vengono riempiti con il bit del segno. Questa differenza fa sì che un valore negativo venga arrotondato all'infinito anziché essere troncato verso zero, il che è un comportamento diverso dalla divisione di numeri interi con segno.
Alcuni minuti di riflessione hanno portato a una soluzione di primo ordine. La soluzione converte in modo condizionale i valori negativi in valori positivi prima dello spostamento. Un valore viene convertito in modo condizionale nella sua forma negativa dopo che è stata eseguita l'operazione di spostamento.
int a = -5;
int n = 1;
int negative = q < 0;
a = negative ? -a : a;
a >>= n;
a = negative ? -a : a;
Il problema con questa soluzione è che le istruzioni di assegnazione condizionale vengono solitamente tradotte in almeno un'istruzione di salto e le istruzioni di salto possono essere costose per i processori che non decodificano entrambi i percorsi di istruzione. Dover ri-innescare una pipeline di istruzioni due volte fa una buona ammaccatura in qualsiasi miglioramento delle prestazioni ottenuto spostando sulla divisione.
Detto quanto sopra, mi sono svegliato sabato con la risposta al problema di assegnazione condizionale. Il problema di arrotondamento che si verifica quando si esegue un'operazione di spostamento aritmetico si verifica solo quando si lavora con la rappresentazione del complemento a due. Non si verifica con la rappresentazione del proprio complemento. La soluzione al problema prevede la conversione del valore del complemento di due in un valore del complemento di uno prima di eseguire l'operazione di spostamento. Dobbiamo quindi riconvertire il valore del complemento in un valore del complemento a due. Sorprendentemente, possiamo eseguire questo insieme di operazioni senza convertire condizionalmente valori negativi prima di eseguire l'operazione di cambio.
int a = -5;
int n = 1;
register int sign = (a >> INT_SIZE_MINUS_1) & 1
a = (a - sign) >> n + sign;
Il valore negativo di un complemento di due viene convertito in un valore negativo di un complemento sottraendo uno. D'altro canto, il valore negativo del complemento di un individuo viene convertito in un valore negativo del complemento di due aggiungendo uno. Il codice sopra elencato funziona perché il bit di segno viene utilizzato per convertire dal complemento a due al complemento a uno e viceversa . Solo i valori negativi avranno i loro bit di segno impostati; pertanto, il segno variabile sarà uguale a zero quando a è positivo.
Con quanto detto sopra, riesci a pensare ad altri hack bit-saggi come quello sopra che sono entrati nella tua borsa di trucchi? Qual è il tuo hack bit-saggio preferito? Sono sempre alla ricerca di nuovi hack bit-saggio orientati alle prestazioni.