Pow, log, exp et sqrt à point fixe rapide

j'ai un point fixe de la classe (10.22) et j'ai besoin d'un prisonnier, d'un sqrt, exp et une fonction log.

hélas, je ne sais même pas par où commencer. Quelqu'un peut-il me fournir des liens vers des articles utiles ou, mieux encore, me fournir du code?

je suppose qu'une fois que j'ai une fonction exp, il devient relativement facile d'implémenter pow et sqrt comme ils le deviennent.

pow( x, y ) => exp( a * log( x ) ) sqrt (x ) => pow( x, 0.5 )

C'est juste ces fonctions exp et log que je trouve difficiles (comme si je me souviens de quelques-unes de mes règles log, Je ne peux pas me souvenir de beaucoup d'autres choses à leur sujet).

probablement, btw, il y aurait aussi une méthode plus rapide pour sqrt et pow donc n'importe quels pointeurs sur ce front serait apprécié même si son juste pour dire utiliser les méthodes que je souligne ci-dessus:)

Veuillez noter: Ce doit être de la croix-plate-forme et dans le plus pur code C/C++ donc je ne peut pas utiliser de l'assembleur optimisation.

18
demandé sur hammar 2011-01-11 15:12:41

4 réponses

une solution très simple est d'utiliser une approximation correcte basée sur une table. Vous n'avez pas besoin de beaucoup de données si vous réduisez vos entrées correctement. exp(a)==exp(a/2)*exp(a/2), ce qui signifie que vous n'avez vraiment besoin de calculer que exp(x)1 < x < 2. Dans cette plage, une approximation runga-kutta donnerait des résultats raisonnables avec ~16 entrées IIRC.

de Même, sqrt(a) == 2 * sqrt(a/4) ce qui signifie que vous n'avez besoin que des entrées de tableau pour 1 < a < 4. Log(a) est un peu plus difficile: log(a) == 1 + log(a/e). C'est plutôt lente itération, mais log(1024) est seulement 6.9 de sorte que vous n'aurez pas beaucoup d'itérations.

vous utiliseriez un algorithme similaire" integer-first " pour pow:pow(x,y)==pow(x, floor(y)) * pow(x, frac(y)). Cela fonctionne parce que pow(double, int) est trivial (diviser pour régner).

[edit] Pour la partie intégrante de la log(a), il peut être utile de stocker un tableau 1, e, e^2, e^3, e^4, e^5, e^6, e^7 donc vous pouvez réduire log(a) == n + log(a/e^n) par une simple recherche binaire codée en dur de a dans cette table. L'amélioration de 7 à 3 étapes n'est pas énorme, mais cela signifie que vous n'avez qu'à diviser une fois par e^n au lieu de n fois par e.

[Modifier 2] Et pour cette dernière log(a/e^n) terme, vous pouvez utiliser log(a/e^n) = log((a/e^n)^8)/8 - chaque itération produit 3 bits de plus par la lecture de la table. Cela maintient votre code et la taille de la table petite. C'est typiquement du code pour les systèmes embarqués, et ils n'ont pas de grandes caches.

[Modifier 3] Ce n'est pas très intelligent de mon côté. log(a) = log(2) + log(a/2). Vous pouvez simplement stocker la valeur fixe log2=0.30102999566 compter le nombre de zéros à gauche, maj a dans la plage utilisée pour votre table de recherche, et multipliez ce décalage (entier) par la constante de point fixe log2. Peut être aussi bas que 3 instructions.

en utilisant e pour l'étape de réduction vous donne juste un" gentil"log(e)=1.0 constante, mais c'est faux optimisation. 0,30102999566 est une constante aussi bonne que 1,0; les deux sont des constantes de 32 bits en 10.22 point fixe. Utiliser 2 comme constante pour la réduction de portée vous permet d'utiliser un décalage de bits pour une division.

Vous obtenez toujours le truc de modifier 2, log(a/2^n) = log((a/2^n)^8)/8. Fondamentalement, cela vous obtient un résultat (a + b/8 + c/64 + d/512) * 0.30102999566 - avec b,c, D dans la gamme [0,7]. a.bcd est vraiment un nombre octal. Pas une surprise puisque nous avons utilisé 8 comme puissance. (L'astuce fonctionne aussi bien avec une puissance de 2, 4 ou 16.)

[modifier 4] Toujours eu une fin ouverte. pow(x, frac(y)pow(sqrt(x), 2 * frac(y)) et nous avons un décent 1/sqrt(x). Cela nous donne une approche beaucoup plus efficace. Dire frac(y)=0.101 binaire, c'est à dire 1/2 plus de 1/8. Cela signifie que x^0.101(x^1/2 * x^1/8). Mais x^1/2sqrt(x) et x^1/8(sqrt(sqrt(sqrt(x))). Je sauve une opération de plus, Newton-Raphson NR(x) donne-nous!--27-- > donc nous calculons 1.0/(NR(x)*NR((NR(NR(x))). Nous inversons seulement le résultat final, n'utilisez pas la fonction sqrt directement.

21
répondu MSalters 2015-09-10 00:28:02

ci-Dessous est un exemple C mise en œuvre de l'Argile S. Turner point fixe logarithme de base 2 de l'algorithme de[1]. L'algorithme ne nécessite aucune sorte de table de recherche. Cela peut être utile sur les systèmes où les contraintes de mémoire sont étroites et où le processeur n'a pas de FPU, comme c'est le cas avec de nombreux microcontrôleurs. Journal de la base e et log base 10 sont alors également supportés en utilisant la propriété logarithmes qui, pour n'importe quelle base n:

          log (x)
             y
log (x) = _______
   n      log (n)
             y

où, pour cet algorithme, o égale 2.

une caractéristique intéressante de cette implémentation est qu'elle prend en charge la précision variable: la précision peut être déterminée à l'exécution, au détriment de la portée. De la façon dont je l'ai implémenté, le processeur (ou compilateur) doit être capable de faire des maths 64 bits pour contenir des résultats intermédiaires. Il peut être facilement adapté pour ne pas nécessiter de soutien 64 bits, mais la gamme sera réduire.

en utilisant ces fonctions, soit une valeur à point fixe spécifié precision. Par exemple, si precision est de 16, alors x doit être gradué de 2^16 (65536). Le résultat est une valeur à point fixe avec le même facteur d'échelle que l'entrée. Une valeur de retour INT32_MIN représente l'infini négatif. Une valeur de retour INT32_MAX indique une erreur et errno sera réglé à EINVAL, indiquant que la précision d'entrée était invalide.

#include <errno.h>
#include <stddef.h>

#include "log2fix.h"

#define INV_LOG2_E_Q1DOT31  UINT64_C(0x58b90bfc) // Inverse log base 2 of e
#define INV_LOG2_10_Q1DOT31 UINT64_C(0x268826a1) // Inverse log base 2 of 10

int32_t log2fix (uint32_t x, size_t precision)
{
    int32_t b = 1U << (precision - 1);
    int32_t y = 0;

    if (precision < 1 || precision > 31) {
        errno = EINVAL;
        return INT32_MAX; // indicates an error
    }

    if (x == 0) {
        return INT32_MIN; // represents negative infinity
    }

    while (x < 1U << precision) {
        x <<= 1;
        y -= 1U << precision;
    }

    while (x >= 2U << precision) {
        x >>= 1;
        y += 1U << precision;
    }

    uint64_t z = x;

    for (size_t i = 0; i < precision; i++) {
        z = z * z >> precision;
        if (z >= 2U << precision) {
            z >>= 1;
            y += b;
        }
        b >>= 1;
    }

    return y;
}

int32_t logfix (uint32_t x, size_t precision)
{
    uint64_t t;

    t = log2fix(x, precision) * INV_LOG2_E_Q1DOT31;

    return t >> 31;
}

int32_t log10fix (uint32_t x, size_t precision)
{
    uint64_t t;

    t = log2fix(x, precision) * INV_LOG2_10_Q1DOT31;

    return t >> 31;
}

Le code de cette application a également la vie à Github, ainsi qu'un exemple/programme de test qui illustre comment utiliser cette fonction pour calculer et afficher les logarithmes à partir des nombres lus à partir de l'entrée standard.

[1] C.S. Turner, "A Fast Binaire Logarithm Algorithm", IEEE Signal Processing Mag., pp. 124,140, Sep. 2010.

7
répondu Dan Moulding 2014-03-13 12:15:50

Un bon point de départ est Le Livre de Jack Crenshaw,"Mathématiques boîte à outils pour la Programmation en Temps Réel". Il a une bonne discussion des algorithmes et implémentations pour diverses fonctions transcendantes.

5
répondu Paul R 2015-11-16 23:46:20

Vérifier mon point fixe sqrt mise en œuvre en utilisant uniquement les opérations sur entiers. C'était amusant d'inventer. Assez vieux maintenant.

https://groups.google.com/forum/?hl=fr%05aacf5997b615c37&fromgroups#!topic/comp.lang.c/IpwKbw0MAxw/discussion

sinon cochez la case CORDIC ensemble d'algorithmes. C'est la façon d'implémenter toutes les fonctions que vous avez énumérées et les fonctions trigonométriques.

EDIT : je publié la source revue sur GitHub ici

3
répondu chmike 2012-05-23 16:39:45