Je cherche le meilleur algorithme racinaire carré inverse pour les chiffres fixes 16.16. Le code ci-dessous est ce que j'ai jusqu'à présent (mais il faut essentiellement la racine carrée et se divise par le nombre d'origine, et je voudrais obtenir la racine carrée inverse sans division). Si cela change quelque chose, le code sera compilé pour armv5te.
uint32_t INVSQRT(uint32_t n) { uint64_t op, res, one; op = ((uint64_t)n<<16); res = 0; one = (uint64_t)1 << 46; while (one > op) one >>= 2; while (one != 0) { if (op >= res + one) { op -= (res + one); res += (one<<1); } res >>= 1; one >>= 2; } res<<=16; res /= n; return(res); }
3 Réponses :
L'astuce consiste à appliquer la méthode de Newton sur le problème x - 1 / y ^ 2 = 0. Donc, donné x, résolve pour y à l'aide d'un schéma itératif.
Y_(n+1) = y_n * (3 - x*y_n^2)/2
Vous pouvez trouver un point de départ décent à l'aide d'une recherche exponentielle pour trouver un intervalle dans lequel X Y i> Y-1 change de signe, puis utilisez la méthode Secante sur cet intervalle et cet intervalle après cela.
Mais comment ça ressemblerait au code? Pensez-vous que ce serait plus efficace que ce que j'ai déjà?
Ce code double le nombre de chiffres corrects dans votre résultat chaque itération. Donc, si vous travaillez dans 16 chiffres, il faut environ 4 itérations pour converger, si vous commencez avec 1 chiffre correct pour la première approximation. Comment allait-il regarder dans le code? Cela ressemble beaucoup à la ligne unique que j'ai écrite ci-dessus.
Les processeurs d'armv5te fournissent un multiplicateur entier rapide et une instruction "Nombre de zéros". Ils viennent également généralement avec des caches modérément de taille. Sur la base de cela, l'approche la plus appropriée pour une implémentation de haute performance semble être une recherche de table pour une approximation initiale, suivie de deux itérations de Newton-Raphson pour obtenir des résultats entièrement précis. Nous pouvons accélérer la première de ces itérations en outre avec un pré-calcul supplémentaire intégré à la table, une technique utilisée par des ordinateurs de crayon il y a quarante ans.
La fonction Pour pouvoir effectuer ces calculs avec précision, quelle que soit la magnitude de l'entrée, l'argument Le code utilise deux fonctions d'assistance: FXRSQRT () code> ci-dessous implémente ceci approcher. Il commence avec une approximation de 8 bits
R code> à la racine carrée réciproque de l'argument
A code>, mais au lieu de stocker
r code>, chaque élément de table stocke 3R (dans les dix bits inférieurs de l'entrée 32 bits) et R 3 sup> (dans les 22 bits supérieurs de l'entrée 32 bits). Cela permet le calcul rapide de la première itération comme
r 1 sub> = 0,5 * (3 * r - a * r 3 sup>). La deuxième itération est ensuite calculée de manière conventionnelle comme R 2 sub> = 0,5 * r 1 sub> * (3 - R 1 SUB> * (R 1 sub> * a)). p>
A code> est normalisé au début du calcul, en substance le représentant comme un < Code> 2.32 Code> Numéro de point fixe multiplié avec un facteur d'échelle de 2 scal sup>. À la fin du calcul, le résultat est dénormalisé selon la formule 1 / sqrt (2 2n sup>) = 2 -n sup>. En rassemblant les résultats dont le bit jugé le plus significatif est 1, la précision est améliorée, entraînant presque tous les résultats sont arrondis correctement. Les rapports de test exhaustifs:
Résultats trop bas: 639 Trop haut: 1454 Pas correctement arrondi: 2093 Code> P>
__ clz () Code> détermine le nombre de nulles nulles principales dans un argument 32 bits non nul.
__ umulhi () code> calcule les 32 bits les plus significatifs d'un produit complet 64 bits de deux entiers non signés 32 bits. Les deux fonctions doivent être mises en œuvre via le compilateur intrinsique ou en utilisant un peu d'assemblage en ligne. Dans le code ci-dessous, je montrent des implémentations portables bien adaptées aux CPU des bras ainsi que des versions de montage en ligne pour des plates-formes X86. Sur les plates-formes d'armv5te
__ clz () code> doit être mappée mappée sur l'instruction code> CLZ code> et
__ umulhi () code> doit être mappé sur
Oumull code>. p>
J'ai une solution que je caractérise comme "SQRT inverse rapide, mais pour des points fixes 32 bits". Pas de table, pas de référence, juste directement au point avec une bonne supposition.
Si vous le souhaitez, passez au code source ci-dessous, mais méfiez-vous de quelques éléments. P>
(x * y) >> 16 code> peut être remplacé par tout schéma de multiplication à point fixe que vous souhaitez. LI>
- Cela ne nécessite pas 64 bits [mots longs], je viens d'utiliser cela pour la facilité de démonstration. Les mots longs sont utilisés pour empêcher le débordement de la multiplication. Une bibliothèque de mathématiques à point fixe aura des fonctions de multiplication à point fixe qui gèrent cela mieux. LI>
- La supposition initiale est assez bonne, vous obtenez donc des résultats relativement précis dans la première incantation. Li>
- Le code est plus verbeux que nécessaire pour la démonstration. LI>
- Les valeurs inférieures à 65536 (
- Ce n'est généralement pas plus rapide que d'utiliser une table racine carrée et une division si votre matériel a une fonction de division. Si ce n'est pas le cas, cela évite les divisions. LI>
OL>
int fxisqrt(int input){
if(input <= 65536){
return 1;
}
long xSR = input>>1;
long pushRight = input;
long msb = 0;
long shoffset = 0;
long yIsqr = 0;
long ysqr = 0;
long fctrl = 0;
long subthreehalf = 0;
while(pushRight >= 65536){
pushRight >>=1;
msb++;
}
shoffset = (16 - ((msb)>>1));
yIsqr = 1<<shoffset;
//y = (y * (98304 - ( ( (x>>1) * ((y * y)>>16 ) )>>16 ) ) )>>16; x2
//Incantation 1
ysqr = (yIsqr * yIsqr)>>16;
fctrl = (xSR * ysqr)>>16;
subthreehalf = 98304 - fctrl;
yIsqr = (yIsqr * subthreehalf)>>16;
//Incantation 2 - Increases precision greatly, but may not be neccessary
ysqr = (yIsqr * yIsqr)>>16;
fctrl = (xSR * ysqr)>>16;
subthreehalf = 98304 - fctrl;
yIsqr = (yIsqr * subthreehalf)>>16;
return yIsqr;
}
Pédangue: Vraisemblablement, vous signifie RACINATEUR SQUIRE I> SQUARE?
en.wikipedia.org/wiki/fast_inverse_square_root ?
^ Exactement j'étais sur le point de répondre avec ça
@Guerrero, @jonathan: Oui, le nom de l'article est trompeur (en effet que l'article indique "la racine carrée inverse rapide ... est une méthode de calcul de la réciprocité d'une racine carrée"). La racine carrée inverse est juste en place!