Come eseguire l'approssimazione di piccoli valori per sqrt (x) su FPGA


8

Sto cercando di implementare una routine a virgola fissa che prevede il calcolo del valore di x per i piccoli xche si avvicina a . L'architettura di destinazione è un FPGA. Un problema è che questa funzione non si presta facilmente all'uso dell'espansione di Taylor. Si può vedere che per piccoli valori di x, la pendenza di va all'infinito quando avvicina a , quindi valutare la funzione usando una serie di potenze implica moltiplicare enormi coefficienti con una piccola . Questo metodo è quindi numericamente instabile.0xx0x

Utilizzando un approccio iterativo, Newton-Raphson produce la seguente equazione iterativa: , dove siamo cercando di approssimare . Ma ancora una volta, dato che è piccolo, dovrebbe anche essere piccolo perché la soluzione converga. Poiché l'equazione implica la divisione di un numero piccolo per un altro numero piccolo, è probabile che l'aritmetica in virgola fissa fallisca.xn+1=xn2α2xnααxn

Con ciò, vorrei sapere come implementare un'approssimazione di piccolo valore per usando l'aritmetica a punto fisso, usando coefficienti precomputati o metodi iterativi.x


2
Se stai prendendo di mira un FPGA, la prima e più importante domanda è quale precisione desideri. Dici di voler usare il punto fisso: quale precisione per l'input, quale precisione per il risultato? Nel punto fisso (come in numeri interi) non c'è "avvicinamento a zero". C'è solo un numero minimo a cui sei interessato.
Philippe

Risposte:


5

Una routine che ho usato prima (non so se sia "corretta" o meno) è un approccio di divisione e conquista.

Inizi con un valore superiore e inferiore arbitrario (diciamo rispettivamente 5 e 0 - le radici quadrate più alte e più basse che vuoi trovare) e trovi il punto medio tra loro. Square quel valore.

Se il valore al quadrato è maggiore del target, impostare il valore superiore in modo che corrisponda al valore al quadrato. Se è inferiore, imposta il valore più basso.

Ripeti fino a quando il valore quadrato non corrisponde al valore di ricerca o non hai eseguito abbastanza iterazioni per essere preciso come desideri.

Ecco una piccola versione che ho messo insieme in perl:

#!/usr/bin/perl

my $val = shift;

my $max = 5;
my $min = 0;

my $iterations = 0;
my $maxiter = 40;

while(($max > $min) and ($iterations<$maxiter))
{
    $iterations++;
    my $diff = $min + ($max - $min) / 2;
    my $square = $diff * $diff;

    if($square == $val)
    {

        print "Square root found at $diff\n";
        print "$iterations iterations\n";
        exit(0);
    } else {
        if($square > $val)
        {
            $max = $diff;
        } else {
            $min = $diff;
        }
    }
}

my $diff = $min + ($max - $min) / 2;
print "Approximate square root after $iterations iterations: $diff\n";

Questo ovviamente sta usando il virgola mobile, ma potrebbe facilmente essere aggiunto al punto fisso. È possibile variare la precisione modificando il limite di iterazione. Ogni iterazione diventa leggermente più accurata di quella precedente.

ad es .: - trova la radice quadrata di 9:

Approximate square root after 40 iterations: 2.99999999999955
   - or - 
Approximate square root after 10 iterations: 3.00048828125
   - or - 
Approximate square root after 5 iterations: 3.046875

Se avesse trovato il valore 3, si sarebbe fermato presto, ovviamente.

Dagli abbastanza iterazioni e dovrebbe renderlo molto preciso:

./sqrt.pl 0.00284
Square root found at 0.0532916503778969
59 iterations

2
Fondamentalmente una ricerca binaria.
rfusca,

Conosci un metodo per scegliere il valore iniziale?
Ang Zhi Ping,

È la radice quadrata del numero più grande che ti aspetti di affrontare.
Majenko,


3

Non hai specificato cosa intendi per "piccolo valore" o "approssimazione". Quindi quello che sto per proporre potrebbe non funzionare, ma qui va.

La cosa più semplice sarebbe quella di creare una tabella di consultazione. Essenzialmente una ROM in cui il bus dell'indirizzo è il numero che si desidera con radice quadrata e l'output dei dati è il risultato. Con un singolo BRAM, è possibile eseguire una LUT a 9 bit in, 8 bit in out. Naturalmente, più BRAM ti daranno un tavolo più grande.

(BRAM = Il termine Xilinx per un blocco RAM, che può anche essere usato come una ROM. Altri FPGA hanno cose simili.)

Se si desidera una precisione maggiore di quella fornita da BRAM, è possibile eseguire una semplice interpolazione lineare di due voci LUT. Ad esempio, supponiamo che tu voglia un input a 12 bit, ma hai solo BRAM per 10 bit. Prendi i primi 10 bit del tuo input e lo cerchi nella LUT. Aggiungi 1 a quei 10 bit e cerca anche quel valore. Quindi fai una semplice interpolazione lineare tra i due risultati, usando i 2 bit inferiori per dirti la proporzione di un valore rispetto all'altro. Naturalmente questo ti darà solo un'approssimazione, ma penso che se fai la matematica scoprirai che potrebbe essere abbastanza buono.

Questo metodo è il meno accurato con numeri di valore basso, ma poiché l'ingresso passa a valori più alti l'accuratezza aumenta.

Un'ottimizzazione del metodo sopra sarebbe quella di utilizzare i BRAM come ROM a doppia porta. In questo modo è possibile leggere due valori senza aumentare il numero di BRAM utilizzati. Ciò consentirà anche di calcolare un SQRT per ogni ciclo di clock, con alcuni ritardi nella pipeline.

Per inciso, questo metodo funziona anche per SINE / COSINE!


Piccolo valore significa che x si avvicina a 0, ecco perché sono interessante nell'approssimazione di piccolo valore di \ sqrt {x}.
Ang Zhi Ping

1
@angzhiping "Approaching zero" non aiuta. Dobbiamo conoscere la gamma e la precisione. Ciò che hai dato è metà dell'intervallo e nessuna precisione. Il risultato finale è conoscere il numero di bit di input e output. Importante è anche la velocità richiesta: in termini di velocità di clock e clock per sqrt.

3

Prova il seguente approccio

  • Se il numero è negativo, gestirlo di conseguenza.
  • Se il numero è 0, restituisce 0.
  • Altrimenti:
  • normalizza su un numero nell'intervallo [1/4, 1]: conta quante volte k devi moltiplicare il tuo numero per 4 ( x <<= 2in C) fino a quando non rientra nell'intervallo sopra.
  • utilizzare un approccio arbitrario (approssimazioni polinomiali, metodo di Newton per sqrt a [n] = (a [n-1] + k / a [n-1]) / 2, ecc.) per calcolare la radice quadrata all'interno di questo intervallo
  • denormalizza: sposta a destra di k bit

0

Provare x=(y+d)2y2+2dy quindi lascia d=(xy2)/2y=(x/yy)1 e poi y=y+d. Se MSb è n da destra, per prima cosay=1(n/2). Converge in <4 iterazioni.


0

Prova: ipotesi migliorata per la prima variabile

Il tuo numero può essere considerato: A * 2 ^ n
La prima approssimazione è quindi: A * 2 ^ (n / 2)

Supponi di utilizzare un numero a 32 bit, con 24 bit utilizzati per contenere le frazioni. Per numeri> 1:
1. Contare il numero di bit utilizzati nella parte intera (N)
2. Dimezzare questo numero (N '= N / 2, ovvero spostato a destra di 1 bit)
3. Spostare a destra il numero originale di N' : questa è la tua prima ipotesi.

In questo formato, il numero più piccolo che puoi avere è 2 ^ -24. La radice quadrata sarà di circa 2 ^ -12. Quindi, per i numeri <1:
1. Contare il numero di bit "zero" nella frazione, fino a raggiungere un bit impostato (N)
2. Dimezzare questo numero (N '= N / 2, cioè spostato a destra di 1 bit)
3. SINISTRA sposta il numero originale del conteggio rivisto: questa è la tua prima ipotesi.

Esempio:
0,0000 0000 0000 0000 1 [16 zero iniziali] approssimativo a: 0,0000 0000 1

Infine, se hai ancora problemi con la piccola A: puoi calcolare 1 / A?
In tal caso, inverti il ​​tuo numero, quindi prova a utilizzare l'algoritmo Radice inversa quadrata:
x' = 0.5x * (3 - Ax^2)

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.