Qual è la tua tecnica bit-saggia preferita? [chiuso]


14

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.


3
Questa domanda e il nome del tuo account - il mondo ha di nuovo senso ...
JK

+1 Interessante domanda come seguito per il mio e anche per il resto;)
Anto

Una volta ho anche eseguito alcuni rapidi calcoli della parità. La parità è un po 'una seccatura perché tradizionalmente comporta dei loop e il conteggio se un po' è impostato, il che richiede molti salti. La parità può essere calcolata usando shift e XOR, quindi un gruppo di quelli fatti uno dopo l'altro evita i loop e i salti.
quick_now

2
Sai che esiste un intero libro su queste tecniche? - Hackers Delight amazon.com/Hackers-Delight-Henry-S-Warren/dp/0201914654
nikie

Sì, c'è anche un sito web dedicato alle operazioni bit. Ho dimenticato l'URL, ma Google lo visualizzerà abbastanza presto.
quick_now

Risposte:


23

Adoro l'hack di Gosper (HAKMEM # 175), un modo molto astuto di prendere un numero e ottenere il numero successivo con lo stesso numero di bit impostati. È utile, ad esempio, per generare combinazioni di kelementi da ncosì:

int set = (1 << k) - 1;
int limit = (1 << n);
while (set < limit) {
    doStuff(set);

    // Gosper's hack:
    int c = set & -set;
    int r = set + c;
    set = (((r^set) >>> 2) / c) | r;
}

7
+1. Ma da ora in poi, avrò incubi sul trovare questo durante una sessione di debug senza il commento.
nikie l'

@nikie, muahahahaha! (Tendo a usarlo per cose come i problemi di Project Euler - il mio lavoro diurno non comporta molta combinatoria).
Peter Taylor,

7

Il metodo della radice quadrata inversa veloce utilizza le più bizzarre tecniche a livello di bit per calcolare l'inverso di una radice quadrata che io abbia mai visto:

float Q_rsqrt( float number )
{
    long i;
    float x2, y;
    const float threehalfs = 1.5F;

    x2 = number * 0.5F;
    y  = number;
    i  = * ( long * ) &y;                       // evil floating point bit level hacking [sic]
    i  = 0x5f3759df - ( i >> 1 );               // what the fuck? [sic]
    y  = * ( float * ) &i;
    y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
    //    y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

    return y;
}

Anche lo sqrt veloce è sorprendente. Carmack sembra essere uno dei più grandi programmatori.
BenjaminB,

Wikipedia ha fonti ancora più vecchie, ad es. Beyond3d.com/content/articles/15
MSalters,

0

Divisione per 3 - senza ricorrere a una chiamata della libreria di runtime.

Si scopre che la divisione per 3 (grazie a un suggerimento su Stack Overflow) può essere approssimata come:

X / 3 = [(x / 4) + (x / 12)]

E X / 12 è (x / 4) / 3. C'è un elemento di ricorsione che improvvisamente appare qui.

Si scopre anche che se limiti l'intervallo dei numeri in cui stai giocando, puoi limitare il numero di iterazioni necessarie.

E quindi, per numeri interi senza segno <2000, il seguente è un algoritmo veloce e semplice / 3. (Per numeri più grandi, basta aggiungere più passaggi). I compilatori ottimizzano il tutto in modo che finisca per essere veloce e piccolo:

short senza segno statico FastDivide3 (const senza segno short arg)
{
  runningSum corto non firmato;
  short frazionario senza segno;

  RunningSum = arg >> 2;

  FractionalTwelth = RunningSum >> 2;
  RunningSum + = FractionalTwelth;

  FractionalTwelth >> = 2;
  RunningSum + = FractionalTwelth;

  FractionalTwelth >> = 2;
  RunningSum + = FractionalTwelth;

  FractionalTwelth >> = 2;
  RunningSum + = FractionalTwelth;

  // Più ripetizioni delle 2 righe precedenti per una maggiore precisione

  restituire RunningSum;
}

1
Naturalmente, questo è rilevante solo per microcontrollori molto oscuri. Qualsiasi CPU reale realizzata negli ultimi due decenni non ha bisogno di una libreria di runtime per la divisione di numeri interi.
MSalters l'

1
Oh certo, ma i piccoli micro senza un moltiplicatore hardware sono in realtà molto comuni. E se lavori in terreni incastonati e vuoi risparmiare $ 0,10 su ciascuno dei milioni di prodotti venduti, allora conoscerai meglio alcuni trucchi sporchi. Quel denaro risparmiato = profitto extra che rende il tuo capo molto felice.
quick_now

Bene, sporco ... si sta solo moltiplicando per .0101010101(circa 1/3). .000100010001101.010101010101
Suggerimento

Come potrei farlo con solo numeri interi e senza virgola mobile?
quick_now

1
Bit a bit, x * 101 = x + x << 2. Allo stesso modo, x * 0,000100010001 è x >> 4 + x >> 8 + x >> 12.
MSalters

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.