Author Topic: Classe mathématique  (Read 5228 times)

0 Members and 1 Guest are viewing this topic.

Offline azerty

  • Base
    • View Profile
  • Ancienneté: 1994
  • Rôle: Code
Classe mathématique
« on: 20 December 2012 à 07:34:31 »
Bonjour,

J'utilise une petite classe mathématique orientée ASM et j'aimerais avoir des conseils sur la manière de
l'améliorer. J'utilise OpenGL et je connais glm mais je ne l'utilise pas.



m.h:

class m
{
   private:
      m();
      virtual ~m();

   public:
      /** Convert degrees to radians */
      static float radians(float degrees) { return (degrees *  0.01745329f); }

      /** Convert radians to degrees */
      static float degrees(float radians) { return (radians * 57.29577951f); }

      /** Return sinus of angle (radians) */
      static float sin(float a);

      /** Return cosinus of angle (radians) */
      static float cos(float a);

      /** Return square root of x */
      static float sqrt(float x);

      /** Return absolute value of x */
      static float abs(float x);

      /** Returns the value of a raised to the power of b */
      static float pow(float a, float b);

      /**   Swap a and b */
      static void swap(float& a, float& b);

      /** Linear interpolation */
      static float lerp(float a, float b, float x);
}

m.cpp:

m::m()
{
}

m::~m()
{
}

float m::sin(float a)
{
   volatile float res;

   __asm
   {
      fld a
      fsin
      fstp res
   }

   return res;
}

float m::cos(float a)
{
   volatile float res;

   __asm
   {
      fld a
      fcos
      fstp res
   }

   return res;
}

float m::sqrt(float a)
{
   volatile float res;

   __asm
   {
      fld a
      fsqrt
      fstp res
   }

   return res;
}

float m::abs(float x)
{
   return (x > 0) ? x : -x;
}

float m::pow(float a, float b)
{
   return powf(a, b);
}

void m::swap(float& a, float& b)
{
   float t = a;
   a = b;
   b = t;
}

float m::lerp(float a, float b, float x)
{
   return (a * (1.f - x) + b * x);
}




Merci d'avance pour toute réponse,
azerty

Offline ponce

Re : Classe mathématique
« Reply #1 on: 20 December 2012 à 10:55:20 »
- enlever l'asm pour remplacer par des intrinsics
- utiliser un namespace au lieu d'une classe avec que des fonctions static
- enlever constructeur et destructeur même si tu fais pas l'item du dessus
- perso j'enlèverais sin/cos/abs/sqrt/pow pour remplacer par des fonctions standards (et éventuellement si c'est ca le bottleneck un jour, les remplacer)
- toutes ces fonctions tu peux les rendre génériques, comme ça le jour ou tu veux lerp ou swap des matrices tu peux.

Offline azerty

  • Base
    • View Profile
  • Ancienneté: 1994
  • Rôle: Code
Re : Classe mathématique
« Reply #2 on: 20 December 2012 à 11:23:04 »
Ponce,

- enlever l'asm pour remplacer par des intrinsics

Je lis sur le site de Microsoft: "An intrinsic is often faster than the equivalent inline assembly".
Je vais donc me pencher sur la question.

- utiliser un namespace au lieu d'une classe avec que des fonctions static

Que gagne-t-on à faire comme ça?


Merci d'avance...

Offline ponce

Re : Classe mathématique
« Reply #3 on: 20 December 2012 à 11:43:47 »
Que gagne-t-on à faire comme ça?

A vrai dire tu ne gagnes rien mais tu ne gagnes rien non plus en mettant tout ça dans une classe.
Ca prête un peu à confusion le destructeur virtuel car ta classe n'est pas destinée à être héritée.

edit: ouais l'assembleur inline potentiellement ça désactive pas mal d'optimisation autour, alors que les intrinsics s'intègre bien avec le compilo.

Offline MsK`

  • Base
    • Pouet.net
    • View Profile
  • Rôle: Code
  • Ville: Paris/RP
Re : Classe mathématique
« Reply #4 on: 20 December 2012 à 12:39:27 »
Sinon pour le lerp : a + (b - a) * x.

Offline h0bby1

  • Base
    • View Profile
Re : Classe mathématique
« Reply #5 on: 17 February 2013 à 11:23:16 »
tout depend quel est ton objectif pour creer ces fonction en assembler, si tu veux eviter les dependences vers la libC du compilo pour des raison de taille ou de portabilitée, ou si c'est pour la vitesse, ou pour faire les operations de facon specifique ou garantir la precision, ou gerer l'allocation memoire pour avoir une meilleur utilisation du cache

comme ca deja été dis, si c'est pour gagner en vitesse, aujourd'hui avec les architectures pipelinée, ce qui compte est pas uniquement le nombre de cycles que l'instruction prend pour s'executer , mais aussi la facon donc le cpu va pouvoir executer les operation successive en meme temps dans different pipeline, donc generalement, l'optimisation prend plus de sens si tu optimise entierement la boucle plutot qu'optimiser chaque operation séparement, car l'optimisation optimale peut dependre grandement du contexte, et le compilo peut en general mieux se demerder pour optimiser les boucles entieremenet a partir du code C de toute la boucle

dans le cas de ces instruction que tu a posté, le inline deja s'impose, pas _asm() inline, mais delcarer la fonction en 'inline void function' XX pour le compilo colle le code de la fonction au lieu de faire l'appel, apres question entre template, fonction C ou namespace, niveau perf ca doit pas changer grand chose, l'avantage du C est que tu peux facilement remplacer la fonction dans un fichier assembler externe, ou voir utiliser les type d'appel __fastcall pour passer les parametre sur des registres au lieu d'utiliser la pile, et les conventions d'apels sont plus standard, si tu dois la compiler en dll par example, et pouvoir la lier avec du code compilé avec un compilo different, avec des fonction C standard en __cdecl le linkage est bcp plus simple et beaucoup plus compatible, et beaucoup plus facile a interfacer avec des fonctions en assembler externe

niveau perf ce qui vaut vraiment le coup c'est surtout les operation vectorielle avec le sse, dans le cas, il peut devenir important d'avoir la zone memoire du vecteur aligné, pareil pour les matrices, si t'as des vecteurs 3D, mieux vaut les declarer par 4 float aligné sur 16 octets, meme si tu utilises que 3 valeurs, pour les matrices 3x3, je demande meme si ce serai pas mieux de les declarer en 3x4 pour pouvoir charger chaque ligne de facon alignée avec le sse


float m::sin(float a)
{
   volatile float res;

   __asm
   {
      fld a
      fsin
      fstp res
   }

   return res;
}


normalement dans ce genre de fonction, t'as meme pas a faire le dernie 'fstp res' dans la convention d'appel C, la valeur de retour est placée sur la pile de registre fpu, donc t'as juste qu'a laisser le resultat sur la pile de registre FPU, et tu peux virer directement le fstp res/return res, meme si comme deja mentionné, c'est pas toujours extrement judicieux de coder de genre de fonction en asm inliné pour pas mal de raison, soit faut mieux les coller dans un fichier assembleur externe, soit utiliser les fonctions intrinseques, volatile est aussi une tres mauvaise idée , volatile veux dire que la variable va etre recoller sur les registre a chaque acces, c'est normalement utilisé pour le multi threading, en l'occurence ca n'as aucun interet de la declarer en volatile sur la pile car la fonction sera le seul code a y avoir acces, et en l'occurence, t'as meme pas besoin de declarer la variable res du tout, suffit d'enlever le 'fstp res/return res' et ca roule


pour le abs tu peux utiliser un masque en entier pour vrier le bit de sign du genre

float fabs (float a)
{
   return ((unsigned int)(a)|0x80000000)
}

(enfin la je suis pas sur que cette fonction marche tel quel, mais le principle est ok)

pour les racines carrée et cosinus, ya moyen d'utiliser des somme de fractions, dans quake3 il utilise ca pour calculer les racines carrées et faire les normalisations de vecteur plus rapidement, ca peut aussi etre utilisé sur les cosinus et sinus, en fait c'est ce que le fpu utilise en interne, le nombre de fractions dans la somme determine la precision, si t'as pas besoin de precision elevée uniquement quelque sommes de fraction peuvent suffire

apres si tu commences a utiliser des jeux d'instructions qui peuvent ne pas etre present sur toutes les machines , type AVX, sse4 ou autre, ca peut devenir une bonne idée d'utiliser des pointeurs de fonction, meme si ca elimine la possibilité au compilo d'inliner la fonction, ca permet de faire un cpu dispactching, pour detecter le type de cpu et eventullement choisir la bonne fonction a utiliser pour telle operation, mais en general, faut mieux optimiser principalement les fonctions de haut niveau, voir les boucles entieres, plutot que d'optimiser chaque instruction assembler, comme ca ca permet de mieux preserver le cache, les instruction fetching, mieux optimiser les pipelines, plutot que de devoir faire des appels comme ca, qui seront pas forcement si optimal que ca dans le contexte ou ils seront appelés


si c'est plus pour eviter les dependences vers le compilo, par ex si tu veux faire une dll qui sera linkable par n'importe quel compilo, ou pour faire des intros en virant le CRT de l'exe, a ce moment je pense faut mieux utiliser des prototype d'appel C, plutot que des classes statiques , enfin en tout cas pour toutes les fonctions C de base comme ca, et utiliser un prefixe pour pas que le compilo les confondent avec les siennes du genre 'static inline my_memcpy();', le mieux pour les faire en assembleur ca reste quand meme d'utiliser un fichier assembleur externe, avec MASM ou NASM, comme ca t'as pas a te prendre la tete avec les synthaxes des differents compilos C qui devront les utiliser, mais ca oblige a utiliser de prototype d'appels C, et empeche d'inlining de la fonction, ou avec les fonctions intrinseques qui sont normalement reconnues par la plupart des grands compilateurs type visual studio/gcc/intel

pour pouvoir inliner les fonction d'une classe, apparement il faut mieux declarer le corps des fonctions directement avec la declaration de la  classe dans la header, a moins d'activer les 'optimisation du programmes complets' , sinon si t'as des fonction externes qui sont appelés d'un module a l'autre, le compilo va pas forcement les inliner correctement, enfin si tu veux utiliser l'inlining de fonction, qui peut qd meme etre critique niveau perf defois, par example si t'as :

class vec3
{
float x,y,z;

vec3 operator+(vec3 &other)
{
  return vec3(this.x+other.x,this.y+other.y,this.z+other.z);
}

};


et

vec3 a,b,c,d;

a = b + c + d;

le compilo peut avoir tendence a faire 'b+c' en premier, stocker le resultat dans une classe intermedaire, et renvoyer cette classe au deuximere operateur, au lieu de faire l'equivalent de

a.x= b.x + c.x + d.x;
a.y= b.y + c.y + d.y;
a.z= b.z + c.z + d.z;

sans l'inline le compilo arrivera pas a cette synthaxe, et il utilisera un objet intermediaire, qui peut eventuellement polluer le cache et pas etre optimal

il fera un truc style

temp= b +c;
a= temp +d;

2 appel, un constructeur appelé en trop et du cache pollué etc en utilisant la synthaxe 'C' les objet temporaires sont plus evidents, mais pour certains type fonctions quand les operations doivent pouvoir etre cumulées les unes aux autres, ca peut etre important d'avoir l'inlning, surtout pour des fonction simples du style les fonctions de base de la libC


si le but de faire ces fonctions est une question de performance, l'inline peut devenir important, car si ta fonction n'est pas alignée, surtout pour des fonctions courtes comme ca, le cpu peut avoir a charger 2 cacheline completes juste pour executer 2 ou 3 instruction, si dans une boucle t'as pas mal d'appels vers des fonctions differentes genre cos/sin/sqrt dans une boucle, et que les fonctions sont pas alignées, ca risque meme de bien polluer le cache de code, comparé a des inlines qui prendrons si ca se trouve meme pas plus de place qu'un appel, etant donné que le coeur de la fonction c'est principalement une seule instruction, l'inline de la fonction peut quand meme jouer de facon significative, ou voir comme deja dit en general optimiser chaque boucle de facon specifique sans utiliser d'appels fonction C du tout

si tu dois inliner des fonctions C, faut les declarer en 'static inline' avec le corps dans l'entete inclu par tout les fichiers qui doivent les appeller, pour les classes , faut declarer le corps des fonctions dans la definition de la classe
« Last Edit: 17 February 2013 à 12:32:51 by h0bby1 »

Offline Patapom

  • Base
    • View Profile
    • www.patapom.com
  • Ancienneté: 1988
  • Groupe: Bomb!
  • Rôle: Coder
  • Ville: Lyon
Re : Classe mathématique
« Reply #6 on: 17 February 2013 à 13:37:10 »
Juste pour dire que le fabs c'est plutôt ça :

float fabs (float a)
{
   return (float&) (((unsigned int&) a) & 0x7FFFFFFF);
}
.  Pom  .

Offline h0bby1

  • Base
    • View Profile
Re : Re : Classe mathématique
« Reply #7 on: 17 February 2013 à 13:49:41 »
Juste pour dire que le fabs c'est plutôt ça :

float fabs (float a)
{
   return (float&) (((unsigned int&) a) & 0x7FFFFFFF);
}
:) j'ai pas verifier, mais ca doit etre ca :) c'est plus efficace car ca evite un branching :)

Offline h0bby1

  • Base
    • View Profile
Re : Classe mathématique
« Reply #8 on: 17 February 2013 à 14:00:35 »
ca c'est la fonction de quake pour calculer 1/sqrtf(x) pour normalizer les vecteurs

The following code is the fast inverse square root implementation from Quake III Arena, stripped of C preprocessor directives, but including the exact original comment text:[2]
float Q_rsqrt( float number )
{
        long i;
        float x2, y;
        const float threehalfs = 1.5F;
 
        x2 = number * 0.5F;
        y  = number;
        i  = * ( long * ) &y;                       // evil floating point bit level hacking
        i  = 0x5f3759df - ( i >> 1 );               // what the fuck?
        y  = * ( float * ) &i;
        y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
//      y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed
 
        return y;
}


c'est basé sur des proprietés mathematiques qui permettent d'approximer n'importe quelle fonction avec une somme de fractions, ya les memes pour tout les fonction math cos/sin/sqrt/exp/pow etc

http://en.wikipedia.org/wiki/Taylor_series je crois que c'est ca, series de taylor

In mathematics, a Taylor series is a representation of a function as an infinite sum of terms that are calculated from the values of the function's derivatives at a single point.

ca peut permettre d'avoir toutes les operations mathematiques codée avec juste des aditions des multiplication et des divisions si t'as la bonne serie, ca peut etre plus rapide et moins precis si tu utilise juste quelque termes, et ca permet de pas avoir a dependre de fonctions complexes du fpu, le fpu utilise ca en interne toute facon, en ajustant le nombre de terme en fonction des settings de precision, la serie converge vers la bonne valeur avec le nombre de termes ajoutés
« Last Edit: 17 February 2013 à 15:03:50 by h0bby1 »

Offline Patapom

  • Base
    • View Profile
    • www.patapom.com
  • Ancienneté: 1988
  • Groupe: Bomb!
  • Rôle: Coder
  • Ville: Lyon
Re : Classe mathématique
« Reply #9 on: 17 February 2013 à 15:02:53 »
C'est pas une série de Taylor dans ce cas-là, mais plus une gruge ajoutée à une méthode de Newton pour trouver les racines d'un polynôme.
C'est très bien expliqué sur wikipédia (notamment le "What the fuck" number ;D) :

http://en.wikipedia.org/wiki/Fast_inverse_square_root
.  Pom  .

Offline h0bby1

  • Base
    • View Profile
Re : Re : Classe mathématique
« Reply #10 on: 17 February 2013 à 15:07:55 »
C'est pas une série de Taylor dans ce cas-là, mais plus une gruge ajoutée à une méthode de Newton pour trouver les racines d'un polynôme.
C'est très bien expliqué sur wikipédia (notamment le "What the fuck" number ;D) :

http://en.wikipedia.org/wiki/Fast_inverse_square_root

http://mathworld.wolfram.com/NewtonsMethod.html

Newton's method, also called the Newton-Raphson method, is a root-finding algorithm that uses the first few terms of the Taylor series of a function  in the vicinity of a suspected root. Newton's method is sometimes also known as Newton's iteration, although in this work the latter term is reserved to the application of Newton's method for computing square roots.

Offline Patapom

  • Base
    • View Profile
    • www.patapom.com
  • Ancienneté: 1988
  • Groupe: Bomb!
  • Rôle: Coder
  • Ville: Lyon
Re : Classe mathématique
« Reply #11 on: 17 February 2013 à 15:30:04 »
Oui, c'est les premiers termes d'une série de Taylor répétés successivement pour arriver à la racine mais c'est pas à proprement parler une série de Taylor... ;D
.  Pom  .

Offline h0bby1

  • Base
    • View Profile
Re : Classe mathématique
« Reply #12 on: 17 February 2013 à 15:39:50 »
ca utilise quand meme les propriétée des series de taylors, mais bon dans le cas, faut mieux utiliser le sse quand c'est possible, les fonctions sse sont de toute facon plus rapide, meme pour des operation scalaires, et ya deja les fonctions sqrt et rsqrt dans le sse, en l'occurence, faut mieux utiliser rsqrtss xmm1,[a] toute les fonction vectorielle faut mieux les faire en sse, et meme les fonction sur les scalaires, le compilo intel il utilise les fonctiosn sse par default au lieu du fpu pour toutes les operations quand c'est activé, et ya les operation minss/maxss et mov conditionel qui permettent d'eviter les branches

mais pour les fonctions plus complexes du genre exp/pow/sin/cos qui sont pas forcement dans le sse, ca peut etre util d'utiliser des series de taylors


http://www.ittc.ku.edu/~marks/cgi-bin/pubs/fastSSE2.pdf ce pdf a l'air pas mal pour avoir les fonctions trigo et exp rapide en sse

We have shown that utilizing Taylor Series can improve the time necessary for computing basic functions such as sine and cosine; it gives accurate results as well, for some
other functions such as exponential, tangent, arcsine, and arccosine. Vectorized versions
of these functions give highly improved function speed, often more than tripling the speed.
The generalized method described can be implemented in other limited applications, as
well. Creating a library of these functions could offer a very integratable solution to the
implementation.
« Last Edit: 17 February 2013 à 15:50:26 by h0bby1 »

Offline h0bby1

  • Base
    • View Profile
Re : Classe mathématique
« Reply #13 on: 17 February 2013 à 16:15:59 »
mais bon en general si t'utilises un compilo decent, ca sert a rien de recoder les fonctions de la libC en assembleur une par une, faut mieux laisser le compilo faire a sa sauce avec sa libC, et optimiser a la rigueur les boucles qui sont vraiment critiques

si c'est pour eviter les dependences vers la libC d'un compilateur, ca peut vite etre gallere, car quand les optimisations sont activiées, le compilateur il va souvent faire des droles de trucs, et coller des appels vers sa libC un peu partout, ou optimiser a sa facon les appels vers la libc, donc faut soit desactiver les optimisations, ou recoder les fonctions specifiques au compilateur que t'utilises pour qu'on puisse linker la dll avec un autre compilateur sans probleme, ou incorporer le runtime en statique

si c'est pour avoir des fonctions maths plus rapide, faut mieux utiliser le sse que l'unité fpu a moins qu'il faut que ca marche sur des cpu < pentium 3, et la plupart du temp, le compilateur il va mieux optimiser la plupart des alogrythmes en utilisant sa propre libC, meme si ca demande d'incorporer le runtime en static ou distribuer la dll du runtime avec l'exe, pour les intros faut mieux eviter car ca augmente la taille de l'exe, dans ce cas ca peut avoir un interet de les recoder comme ca meme si ca gagne pas forcement en performance pour eviter d'avoir une dependence sur la libC specifique au compilo

mais dans le cas ou tu veux vraiment eviter la dependence sur la libC du compilo, faut aussi utiliser les fonction systeme pour le malloc/stdio, a base de CreateFile, et les fonction HEAP sous windows du genre VirtualAlloc ou HeapAlloc pour l'alloc, et les fonction kernel directement a base de open/read/write sous linux, et pour l'alloc sous linux je sais pas

mais ca peut permetre de mettre les fonctions systeme pour les fichiers et la memoire dans une lib separée, et avoir le code principal qui est completement autonome, qui depend d'aucune lib systeme ou dependente du compilateur dans le code principal, sans aucun include de fichier systeme ou de la stdlib, ca peut permettre d'eviter les problemes dans le cas ou des fichiers binaire precompilés doivent etre liés a du code compilé avec un compilateur different sans incorporer le runtime du compilo
« Last Edit: 17 February 2013 à 16:25:03 by h0bby1 »

Offline h0bby1

  • Base
    • View Profile
Re : Classe mathématique
« Reply #14 on: 17 February 2013 à 20:44:13 »
moi sinon j'utilisait ces fonctions, la plupart c'est copié collé du crt de visual ou de gcc


mais bon c'est pas specialement optimisé ou quoi que ce soit, moi c'etait surtout soit pour faire des intros et eviter d'avoir a incorporer le runtime C qui fait quelque dizaine de kilo octets, ou pour eviter les problems inter compilos, du genre un qui compile avec visual, l'autre avec codewarrior et l'autre avec gcc, ca peut creer des problemes, c'est pas forcement tout le temps exactement les memes definitions pile poil niveau des types et definition des prototypes, et pour eviter les trucs funkys des optims du compilo, mais je pense ce sera plutot plus lent , ce qui n'est pas trop grave dans mon cas, car soit c'est des trucs qui sont fait une fois par frame genre calcul de matrices et transformations qui n'ont pas trop besoin d'etre optimisés , sois si je veux optimiser vraiment une fonction je la code entierement en asm

Code: [Select]
;-----------------------------------------------------------------------------
; tan.asm - floating point tangent
;-----------------------------------------------------------------------------
_crt_tan:
fld qword [esp+4]
fptan
ret

_crt_tanf:
fld dword [esp+4]
fptan
ret

_crt_atan:
fld qword [esp+4]
    fld1
fpatan
ret

_crt_atanf:
fld dword [esp+4]
    fld1
fpatan
ret

_crt_sin:
  fld qword [esp+4]
  fsin
ret

_crt_sinf:
  fld dword [esp+4]
  fsin
ret

_crt_cos:
  fld qword [esp+4]
  fcos
ret
_crt_cosf:
    fld dword [esp+4]
    fcos
 ret


_crt_sqrtf:
   fld dword [esp+4]
   fsqrt
 ret


_crt_sqrt:
   fld qword [esp+4]
   fsqrt
ret

__ftol2_sse:
fistp dword[uint32]
mov eax,[uint32]
ret

_crt_ldexp:
    push    ebp
mov     ebp,esp
    sub     esp,8               ; Allocate temporary space
    fild    dword [ebp+16]      ; Load n as integer
    fld     qword [ebp+8]       ; Load real from stack
    fscale                      ; Compute 2 to the n
    fstp    st1                 ; Set new top of stack
    fst     qword [ebp-8]       ; Store result
    mov     esp,ebp             ; Deallocate temporary space
    pop     ebp
ret



;-----------------------------------------------------------------------------
_crt_ceil:
               
push    ebp
    mov     ebp,esp
    sub     esp,4                   ; Allocate temporary space
    fld     qword [ebp+8]       ; Load real from stack
    fstcw   [ebp-2]                 ; Save control word
    fclex                           ; Clear exceptions
    mov     word [ebp-4],0b63h  ; Rounding control word
    fldcw   [ebp-4]                 ; Set new rounding control
    frndint                         ; Round to integer
    fclex                           ; Clear exceptions
    fldcw   [ebp-2]                 ; Restore control word
    mov     esp,ebp                 ; Deallocate temporary space
    pop     ebp
ret


;-----------------------------------------------------------------------------
_crt_frexp:
push    ebp
    mov     ebp,esp
    push    edi                     ; Save register edi
    fld     qword [ebp+8]       ; Load real from stack
    mov     edi,dword [ebp+16]  ; Put exponent address in edi
    ftst                            ; Test st for zero
    fstsw   ax                      ; Put test result in ax
    ; Set flags based on test
    jnz     __frexp1                ; Re-direct if not zero
    st                      ; Set exponent to zero
    jmp     __frexp2                ; End of case
__frexp1:       
fxtract                         ; Get exponent and significand
    fld1                            ; Load constant 1
    fld1                            ; Load constant 1
    fadd st0,st1                           ; Constant 2
    fdiv st0,st1                           ; Significand / 2
    fxch                            ; Swap st, st(1)
    fld1                            ; Load constant 1
    fadd  st0,st1                          ; Increment exponent
    fistp   dword [edi]         ; Store result exponent and pop
__frexp2:   
    pop     edi                     ; Restore register edi
    mov     esp,ebp
    pop     ebp
ret


               
_crt_floor:
    push    ebp
    mov     ebp,esp
    sub     esp,4                   ; Allocate temporary space
    fld     qword [ebp+8]           ; Load real from stack
    fstcw   [ebp-2]                 ; Save control word
    fclex                           ; Clear exceptions
    mov     word [ebp-4],0763h      ; Rounding control word
    fldcw   [ebp-4]                 ; Set new rounding control
    frndint                         ; Round to integer
    fclex                           ; Clear exceptions
    fldcw   [ebp-2]                 ; Restore control word
    mov     esp,ebp
    pop     ebp
ret

_crt_expf:
   FLD dword[esp+4]         ;    {  x         -         -    }
   FLDL2E ; { log2(e)    x         -    }
   FMULP ST1, ST0 ; { x.log2(e)  -         -    }
   FSTCW [control_ww]
   FLDCW [round_down]
   FLD ST0      ;   {  z         z          -   }
   FRNDINT ;   { int(z)     z          -   }
   FLDCW [control_ww]
   FXCH ;   {  z         i          -   }
   FSUB ST0, ST1 ;   {  f         i          -   }
   F2XM1 ;   { 2^f-1      i          -   }
   FLD1 ;   {  1        2^f-1       i   }
   FADDP ST1, ST0 ;   { 2^f        i          -   }
   FSCALE ;   { 2^f.2^i    i          -   }
   FSTP ST1 ;   { 10^x       -          -   }
   FWAIT
ret


_crt_exp:
   FLD qword[esp+4]         ;    {  x         -         -    }
   FLDL2E ; { log2(e)    x         -    }
   FMULP ST1, ST0 ; { x.log2(e)  -         -    }
   FSTCW [control_ww]
   FLDCW [round_down]
   FLD ST0      ;   {  z         z          -   }
   FRNDINT ;   { int(z)     z          -   }
   FLDCW [control_ww]
   FXCH ;   {  z         i          -   }
   FSUB ST0, ST1 ;   {  f         i          -   }
   F2XM1 ;   { 2^f-1      i          -   }
   FLD1 ;   {  1        2^f-1       i   }
   FADDP ST1, ST0 ;   { 2^f        i          -   }
   FSCALE ;   { 2^f.2^i    i          -   }
   FSTP ST1 ;   { 10^x       -          -   }
   FWAIT ;   { cc 28/05/98     }
ret

_crt_logf:
     push    ebp
     mov     ebp,esp
     fld     dword [ebp+8]       ; Load real from stack
     fldln2                          ; Load log base e of 2
     fxch    st1                   ; Exchange st, st(1)
fyl2x                           ; Compute the natural log(x)
     pop     ebp
ret


_crt_log:
     push    ebp
     mov     ebp,esp
     fld     qword [ebp+8]       ; Load real from stack
     fldln2                          ; Load log base e of 2
     fxch    st1                   ; Exchange st, st(1)
fyl2x                           ; Compute the natural log(x)
     pop     ebp
ret

_crt_log10:
push    ebp
    mov     ebp,esp
    fld     qword [ebp+8]       ; Load real from stack
    fldlg2                          ; Load log base 10 of 2
    fxch    st1                   ; Exchange st, st(1)
    fyl2x                           ; Compute the log base 10(x)
    pop     ebp
ret







Code: [Select]

static __inline float libc_fabsf(float a)
{
unsigned int t;
t=*(((unsigned int *)(&a)))&0x7FFFFFFF;
return *((float *)(&t));
}

float crt_asinf  ( float A ){
 
  if (A>1.0f||A<-1.0f)return 0.0f;
  if (A==1.0f)   return mLibPif/2.0f;
  if (A==-1.0f)return mLibPif/-2.0f;
  return crt_atan(A/crt_sqrtf(1.0f-A*A));
}
float crt_acosf ( float A )
{
  if (A>1.0f||A<-1.0f) return 0.0f;
  if (A==0.0f)    return mLibPif/2.0f;
  A=crt_atanf(crt_sqrtf(1.0f-A*A)/A);
  if (A<0.0f)A+=mLibPif;
  return A;
}
double crt_asin  ( double A ){
 
  if (A>1.0||A<-1.0)return 0.0;
  if (A== 1.0)   return mLibPi/2.0;
  if (A==-1.0)   return mLibPi/-2.0;
  return crt_atan(A/crt_sqrt(1.0-A*A));
}
double crt_acos ( double A )
{
  double t,tt;
  if (A>1.0||A<-1.0)   return 0.0;
  if (A==0.0)    return mLibPi/2.0;
  t=1.0-A*A;
  tt=crt_sqrt(t)/A;
  A=crt_atan(tt);

  if (A<0.0)A+=mLibPi;
  return A;

}




mais bon en faisant comme ca avec des fonction assembler externe, ca empeche le inlining des fonctions, donc c'est pas specialement plus rapide dans la plupart des cas je pense,  donc ca a pas vraiment d'interet a part pour eviter les problemes de compatibilité inter compilos ou faire des intros
« Last Edit: 17 February 2013 à 20:59:49 by h0bby1 »