stringtranslate.com

algoritmo BKM

El algoritmo BKM es un algoritmo de desplazamiento y suma para calcular funciones elementales , publicado por primera vez en 1994 por Jean-Claude Bajard, Sylvanus Kla y Jean-Michel Muller. BKM se basa en calcular logaritmos complejos ( modo L ) y exponenciales ( modo E ) utilizando un método similar al algoritmo que Henry Briggs utilizó para calcular logaritmos. Al utilizar una tabla precalculada de logaritmos de potencias negativas de dos, el algoritmo BKM calcula funciones elementales utilizando únicamente operaciones de suma, desplazamiento y comparación de enteros.

BKM es similar a CORDIC , pero utiliza una tabla de logaritmos en lugar de una tabla de arcotangentes . En cada iteración, se elige un coeficiente a partir de un conjunto de nueve números complejos, 1, 0, −1, i, −i, 1+i, 1−i, −1+i, −1−i, en lugar de solo −1 o +1 como lo usa CORDIC. BKM proporciona un método más sencillo para calcular algunas funciones elementales y, a diferencia de CORDIC, BKM no necesita un factor de escala de resultados. La tasa de convergencia de BKM es de aproximadamente un bit por iteración, como CORDIC, pero BKM requiere más elementos de tabla precalculados para la misma precisión porque la tabla almacena logaritmos de operandos complejos.

Al igual que con otros algoritmos de la clase de desplazamiento y suma, BKM se adapta particularmente bien a la implementación de hardware. El rendimiento relativo de la implementación de software BKM en comparación con otros métodos, como las aproximaciones polinómicas o racionales , dependerá de la disponibilidad de desplazamientos rápidos de varios bits (es decir, un desplazador de barril ) o aritmética de punto flotante de hardware.

Descripción general

Para resolver la ecuación

el algoritmo BKM aprovecha una propiedad básica de los logaritmos

Usando la notación Pi , esta identidad se generaliza a

Debido a que cualquier número puede representarse mediante un producto, esto nos permite elegir cualquier conjunto de valores que se multiplican para dar el valor con el que comenzamos. En los sistemas informáticos, es mucho más rápido multiplicar y dividir por múltiplos de 2, pero como no todos los números son múltiplos de 2, usar es una mejor opción que una elección más simple de . Dado que queremos comenzar con cambios grandes y ser más precisos a medida que aumentamos, podemos usar más específicamente , permitiendo que el producto se acerque a cualquier valor entre 1 y ~4,768, dependiendo de qué subconjunto usemos en el producto final. En este punto, la ecuación anterior se ve así:

Esta elección reduce la complejidad computacional del producto desde la multiplicación repetida hasta la simple suma y el desplazamiento de bits según la implementación. Finalmente, al almacenar los valores en una tabla, calcular la solución también es una simple cuestión de suma. Iterativamente, esto nos da dos secuencias separadas. Una secuencia se acerca al valor de entrada mientras que la otra se acerca al valor de salida :

Dada esta definición recursiva y debido a que es estrictamente creciente, se puede demostrar por inducción y convergencia que

para cualquier . Para calcular el resultado, primero creamos la tabla de referencia.

Luego, la salida se calcula iterativamente según la definición.

para cualquier .

Debido a que el algoritmo anterior calcula tanto la entrada como la salida simultáneamente, es posible modificarlo ligeramente para que sea el valor conocido y el valor que queremos calcular, calculando así el exponencial en lugar del logaritmo. Dado que x se convierte en una incógnita en este caso, el condicional cambia de

a

función logaritmo

Para calcular la función logaritmo (modo L), el algoritmo en cada iteración prueba si . Si es así, calcula y . Después de las iteraciones se conoce el valor de la función con un error de .

Programa de ejemplo para logaritmo natural en C++ (ver A_etabla):

 double log_e ( const double Argumento , const int Bits = 53 ) // 1 <= Argumento <= 4.768462058 { double x = 1.0 , y = 0.0 , s = 1.0 ;                      for ( int k = 0 ; k < Bits ; k ++ ) { doble const z = x + x * s ; si ( z <= Argumento ) { x = z ; y += A_e [ k ]; } s *= 0,5 ; } devolver y ; }                                   

Los logaritmos para bases distintas de e se pueden calcular con un esfuerzo similar.

Programa de ejemplo para logaritmo binario en C++ (ver A_2tabla):

 double log_2 ( const double Argumento , const int Bits = 53 ) // 1 <= Argumento <= 4.768462058 { double x = 1.0 , y = 0.0 , s = 1.0 ;                      for ( int k = 0 ; k < Bits ; k ++ ) { doble const z = x + x * s ; si ( z <= Argumento ) { x = z ; y += A_2 [ k ]; } s *= 0,5 ; } devolver y ; }                                   

El rango de argumentos permitido es el mismo para ambos ejemplos (1 ≤  Argument ≤ 4,768462058…). En el caso del logaritmo de base 2, el exponente se puede dividir por adelantado (para obtener la parte entera) para que el algoritmo se pueda aplicar al resto (entre 1 y 2). Dado que el argumento es menor que 2,384231…, la iteración de k puede comenzar con 1. Trabajando en cualquier base, la multiplicación por s se puede reemplazar con una modificación directa del exponente de coma flotante, restándole 1 durante cada iteración. Esto da como resultado que el algoritmo utilice solo la suma y no la multiplicación.

Funcion exponencial

Para calcular la función exponencial (modo E), el algoritmo en cada iteración prueba si . Si es así, calcula y . Después de las iteraciones se conoce el valor de la función con un error de .

Programa de ejemplo en C++ (ver A_etabla):

 double exp ( const double Argumento , const int Bits = 54 ) // 0 <= Argumento <= 1.5620238332 { double x = 1.0 , y = 0.0 , s = 1.0 ;                     for ( int k = 0 ; k < Bits ; k ++ ) { doble const z = y + A_e [ k ]; si ( z <= Argumento ) { y = z ; x = x + x * s ; } s *= 0,5 ; } devolver x ; }                                     

Tablas para ejemplos

Notas

Referencias

Otras lecturas