Je comprends gcc's --ffast-math
Le drapeau peut considérablement augmenter la vitesse pour les opérations flottantes et va au-delà des normes IEEE, mais je n'arrive pas à trouver d'informations sur ce qui se passe réellement quand il est activé. Quelqu'un peut-il expliquer certains détails et peut-être donner un exemple clair de la façon dont quelque chose changerait si le drapeau était activé ou désactivé?
J'ai essayé de creuser à travers S.O. pour des questions similaires, mais je n'ai rien trouvé qui explique le fonctionnement de ffast-math.
Comme vous l'avez mentionné, cela permet des optimisations qui ne préservent pas la stricte conformité à la norme IEEE.
Voici un exemple:
x = x*x*x*x*x*x*x*x;
à
x *= x;
x *= x;
x *= x;
L'arithmétique en virgule flottante n'étant pas associative, l'ordre et la factorisation des opérations affecteront les résultats en raison de l'arrondi. Par conséquent, cette optimisation n'est pas effectuée sous le comportement strict FP.
Je n'ai pas encore vérifié si GCC procédait à cette optimisation particulière. Mais l'idée est la même.
-ffast-math
fait beaucoup plus que simplement enfreindre la stricte conformité à la norme IEEE.
Tout d’abord, bien sûr, il ne casse pas la stricte conformité à la norme IEEE, permettant par exemple de: la réorganisation d'instructions en quelque chose de mathématiquement identique (idéalement) mais pas exactement identique en virgule flottante.
Deuxièmement, il désactive définir errno
après des fonctions mathématiques à instruction unique, ce qui signifie qu’il faut éviter d’écrire dans une variable locale de thread (cela peut faire une différence de 100% pour ces fonctions sur certaines architectures ).
Troisièmement, cela suppose que tous les calculs sont finis, ce qui signifie qu'aucune vérification de NaN (ou de zéro) n'est effectuée là où elle aurait des effets néfastes. On suppose simplement que cela ne va pas se produire.
Quatrièmement, il permet approximations réciproques pour la division et la racine carrée réciproque.
En outre, il désactive le zéro signé (le code suppose que le zéro signé n’existe pas, même si la cible le prend en charge) et les calculs arrondis, ce qui permet, entre autres, le pliage constant au moment de la compilation.
Enfin, il génère un code qui suppose qu'aucune interruption matérielle ne peut survenir en raison de mathématiques de signalisation/interception (c'est-à-dire, si celles-ci ne peuvent pas être désactivées sur l'architecture cible et par conséquent se produisent, elles ne seront pas traitées). .