Greg Hewgill e IllidanS4 hanno fornito un collegamento con un'eccellente spiegazione matematica. Cercherò di riassumere qui per quelli che non vogliono entrare troppo nei dettagli.
Qualsiasi funzione matematica, con alcune eccezioni, può essere rappresentata da una somma polinomiale:
y = f(x)
può essere esattamente trasformato in:
y = a0 + a1*x + a2*(x^2) + a3*(x^3) + a4*(x^4) + ...
Dove a0, a1, a2, ... sono costanti . Il problema è che per molte funzioni, come la radice quadrata, per un valore esatto questa somma ha un numero infinito di membri, non termina con x ^ n . Ma se ci fermassimo a qualche x ^ n avremmo comunque un risultato con una certa precisione.
Quindi, se abbiamo:
y = 1/sqrt(x)
In questo caso particolare hanno deciso di scartare tutti i membri del polinomio sopra il secondo, probabilmente a causa della velocità di calcolo:
y = a0 + a1*x + [...discarded...]
E il compito è ora di calcolare a0 e a1 in modo che y abbia la minima differenza dal valore esatto. Hanno calcolato che i valori più appropriati sono:
a0 = 0x5f375a86
a1 = -0.5
Quindi, quando lo metti in equazione, ottieni:
y = 0x5f375a86 - 0.5*x
Che è la stessa della riga che vedi nel codice:
i = 0x5f375a86 - (i >> 1);
Modifica: in realtà qui y = 0x5f375a86 - 0.5*x
non è la stessa cosa i = 0x5f375a86 - (i >> 1);
poiché lo spostamento di float come numero intero non solo divide per due ma divide anche l'esponente per due e causa altri artefatti, ma si tratta comunque di calcolare alcuni coefficienti a0, a1, a2 ....
A questo punto hanno scoperto che la precisione di questo risultato non è sufficiente allo scopo. Quindi hanno anche fatto solo un passaggio dell'iterazione di Newton per migliorare l'accuratezza del risultato:
x = x * (1.5f - xhalf * x * x)
Avrebbero potuto eseguire più iterazioni in un ciclo, ciascuna migliorando il risultato, fino a quando non è stata raggiunta la precisione richiesta. Questo è esattamente come funziona in CPU / FPU! Ma sembra che sia bastata una sola iterazione, che è stata anche una benedizione per la velocità. CPU / FPU esegue tutte le iterazioni necessarie per raggiungere la precisione per il numero in virgola mobile in cui è memorizzato il risultato e ha un algoritmo più generale che funziona per tutti i casi.
Quindi, in breve, quello che hanno fatto è:
Usa (quasi) lo stesso algoritmo di CPU / FPU, sfrutta il miglioramento delle condizioni iniziali per il caso speciale di 1 / sqrt (x) e non calcolare fino in fondo alla precisione che CPU / FPU andrà a ma fermarsi prima, quindi guadagnando velocità di calcolo.