Cómo realizar una aproximación de valor pequeño para sqrt (x) en FPGA


8

Estoy tratando de implementar una rutina de punto fijo que implica calcular el valor de X Para pequeños X que se acerca 0 0. La arquitectura de destino es un FPGA. Un problema es que esta función no se presta fácilmente al uso de la expansión de Taylor. Se puede ver que para valores pequeños de x, la pendiente deX va al infinito cuando X enfoques 0 0, por lo tanto, evaluar la función utilizando una serie de potencia implica multiplicar coeficientes enormes con un pequeño X. Por lo tanto, este método es numéricamente inestable.

Usando un enfoque iterativo, Newton-Raphson produce la siguiente ecuación iterativa: Xnorte+1=Xnorte2-α2Xnorte, donde estamos tratando de aproximarnos α. Pero una vez más, ya queα es pequeño, Xnorteigualmente debería ser pequeño para que la solución converja. Como la ecuación implica dividir un número pequeño por otro número pequeño, es probable que la aritmética de punto fijo falle.

Con eso, me gustaría saber cómo implementar una aproximación de valor pequeño para X usando aritmética de punto fijo, ya sea usando coeficientes precalculados o métodos iterativos.


2
Si está apuntando a un FPGA, la primera y más importante pregunta es qué precisión desea. Dices que quieres usar el punto fijo: ¿qué precisión para la entrada, qué precisión para el resultado? En el punto fijo (como en los enteros) no hay "acercándose a cero". Solo hay un número más pequeño que le interesa.
Philippe

Respuestas:


5

Una rutina que he usado antes (no sé si es una "adecuada" o no) es un enfoque de divide y vencerás.

Comienza con un valor arbitrario superior e inferior (digamos 5 y 0 respectivamente, las raíces cuadradas más altas y más bajas que desea encontrar) y encuentra el punto medio entre ellas. Cuadrar ese valor.

Si el valor al cuadrado es mayor que su objetivo, establezca el valor superior para que sea su valor al cuadrado. Si es más bajo, establezca el valor más bajo.

Repita hasta que el valor cuadrado coincida con su valor de búsqueda, o haya ejecutado suficientes iteraciones para ser tan preciso como desee.

Aquí hay una pequeña versión que he reunido en 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";

Esto, por supuesto, está usando coma flotante, pero podría adaptarse fácilmente al punto fijo. Puede variar la precisión cambiando el límite de iteración. Cada iteración se vuelve un poco más precisa que la anterior.

por ejemplo: - encuentra la raíz cuadrada de 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

Si hubiera encontrado el valor 3, por supuesto, se habría detenido antes.

Dale suficientes iteraciones y debería ser muy preciso:

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

2
Básicamente una búsqueda binaria.
rfusca

¿Conoces un método para elegir el valor inicial?
Ang Zhi Ping el

Es la raíz cuadrada del número más grande con el que espera tratar.
Majenko el


3

No especificó lo que quiere decir con "valor pequeño" o "aproximación". Entonces, lo que voy a proponer podría no funcionar, pero aquí va.

Lo más fácil sería hacer una tabla de consulta. Esencialmente, una ROM donde el bus de direcciones es el número que desea raíz cuadrada, y la salida de datos es el resultado. Con un solo BRAM, podría hacer un LUT de 9 bits de entrada y 8 bits de salida. Por supuesto, más BRAM te darán una mesa más grande.

(BRAM = El término Xilinx para un bloque RAM, que también se puede usar como ROM. Otros FPGA tienen cosas similares).

Si desea más precisión de la que BRAM le dará, puede hacer una simple interpolación lineal de dos entradas LUT. Por ejemplo, supongamos que desea una entrada de 12 bits, pero solo tiene BRAM para 10 bits. Usted toma los 10 bits superiores de su entrada y busca eso en la LUT. Agregue 1 a esos 10 bits y busque ese valor también. Luego, realiza una interpolación lineal simple entre los dos resultados, utilizando los 2 bits inferiores para indicar la proporción de un valor sobre el otro. Por supuesto, esto solo le dará una aproximación, pero creo que si hace los cálculos, encontrará que podría ser lo suficientemente bueno.

Este método es el menos preciso con números de bajo valor, pero a medida que la entrada va a valores más altos, la precisión aumenta.

Una optimización del método anterior sería utilizar los BRAM como una ROM de doble puerto. De esta forma, puede leer dos valores sin aumentar el número de BRAM utilizados. Esto también le permitirá calcular un SQRT para cada ciclo de reloj, con algunos retrasos en la canalización.

Por cierto, ¡este método también funciona para SINE / COSINE!


Valor pequeño significa que x se acerca a 0, por eso me interesa la aproximación de valor pequeño de \ sqrt {x}.
Ang Zhi Ping el

1
@angzhiping "Acercarse a cero" no ayuda. Necesitamos conocer el alcance y la precisión. Lo que diste es la mitad del rango y nada de precisión. El resultado final es conocer el número de bits de entrada y salida. También es importante la velocidad requerida: en términos de velocidad de reloj y relojes por metro cuadrado.

3

Prueba el siguiente enfoque

  • Si el número es negativo, maneje en consecuencia.
  • Si el número es 0, devuelve 0.
  • De otra manera:
  • normalizar a un número en el rango [1/4, 1]: cuente cuántas veces k tiene que multiplicar su número por 4 ( x <<= 2en C) hasta que esté dentro del rango anterior.
  • utilice un enfoque arbitrario (aproximaciones polinómicas, método de Newton para sqrt a [n] = (a [n-1] + k / a [n-1]) / 2, etc.) para calcular la raíz cuadrada dentro de este rango
  • desnormalizar: desplazar a la derecha por k bits

0

Tratar X=(y+re)2y2+2rey Entonces deja re=(X-y2)/ /2y=(X/ /y-y)1 y después y=y+re. Si MSb es n desde la derecha, deje primeroy=1(norte/ /2). Converge en <4 iteraciones.


0

Prueba: adivinanzas mejoradas para la primera variable

Su número puede ser considerado: A * 2 ^ n
La primera aproximación es entonces: A * 2 ^ (n / 2)

Supongamos que está utilizando un número de 32 bits, con 24 bits utilizados para contener fracciones. Para números> 1:
1. Cuente el número de bits utilizados en la parte entera (N)
2. Reduzca a la mitad este número (N '= N / 2, es decir, desplazado a la derecha 1 bit)
3. Desplace a la derecha el número original por N' : esta es tu primera suposición.

En este formato, el número más pequeño que puede tener es 2 ^ -24. La raíz cuadrada será de aproximadamente 2 ^ -12. Entonces, para números <1:
1. Cuente el número de bits "cero" en la fracción, hasta alcanzar un bit establecido (N)
2. Reduzca a la mitad este número (N '= N / 2, es decir, desplazado a la derecha 1 bit)
3. IZQUIERDA cambiar el número original por el recuento revisado: esta es su primera suposición.

Ejemplo:
0.0000 0000 0000 0000 1 [16 ceros iniciales] se aproxima a: 0.0000 0000 1

Finalmente, si todavía tiene problemas con la pequeña A: ¿puede calcular 1 / A?
Si es así, invierta su número, luego intente usar el algoritmo Inverse Square Root:
x' = 0.5x * (3 - Ax^2)

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.