Les opérations en virgule flottante en C sont-elles associatives?

L’addition contient mathématiquement la propriété associative:

(a + b) + c = a + (b + c) 

Dans le cas général, cette propriété ne s’applique pas aux nombres à virgule flottante car ils représentent des valeurs avec une précision finie.

Un compilateur est-il autorisé à effectuer la substitution ci-dessus lors de la génération de code machine à partir d’un programme C dans le cadre d’une optimisation? Où dit-on exactement dans la norme C?

Le compilateur n’est pas autorisé à effectuer des “optimisations”, ce qui donnerait une valeur calculée différente de celle calculée en fonction de la sémantique de la machine abstraite.

5.1.2.3 Exécution du programme

[# 1] Les descriptions sémantiques de la présente Norme internationale décrivent le comportement d’une machine abstraite dans laquelle les problèmes d’optimisation ne sont pas pertinents.

[# 3] Dans la machine abstraite, toutes les expressions sont évaluées comme spécifié par la sémantique.

[# 13] Exemple 5 Le réarrangement des expressions en virgule flottante est souvent limité en raison de limitations de précision et de distance. L’implémentation ne peut généralement pas appliquer les règles associatives mathématiques pour l’addition ou la multiplication, ni la règle dissortingbutive, en raison d’une erreur d’arrondi, même en l’absence de débordement et de débordement.

Dans votre exemple:

 (a + b) + c 

ou même sans les parenthèses:

 a + b + c 

on a

  + / \ + c / \ ab 

et le compilateur est tenu de générer du code comme si a est ajouté à b et que le résultat est ajouté à c .

La multiplication en virgule flottante en C n’est pas associative.

 In C, Floating point multiplication is not associative. 

Certaines preuves sont avec ce code C:

Choisissez trois valeurs flottantes aléatoires.
Vérifiez si a*(b*c) n’est jamais égal à (a*b)*c

 #include #include #include using namespace std; int main() { int counter = 0; srand(time(NULL)); while(counter++ < 10){ float a = rand() / 100000; float b = rand() / 100000; float c = rand() / 100000; if (a*(b*c) != (a*b)*c){ printf("Not equal\n"); } } printf("DONE"); return 0; } 

Le programme imprime:

 Not equal Not equal Not equal Not equal DONE RUN FINISHED; exit value 0; real time: 10ms; user: 0ms; system: 0ms 

Conclusion:

Pour mon test, trois valeurs de multiplication en virgule flottante sélectionnées au hasard sont associatives environ 70% du temps.

Vous pouvez associer des opérations en virgule flottante aux options gcc:

-funsafe-math-optimizations -O2

Exemple: double test (double a, double b, double c) {
retour (a + b + c) * (a + (b + c)); }

Ceci est réduit à: double temp = a + (b + c); return temp * temp;

De même, (a + b + c) – (a + (b + c)) est réduit à zéro, en ignorant la possibilité de INF et NAN.

Si je comstack plutôt avec -fassociative-math -O2, j’obtiens le message étrange: “warning: -fassociative-math disabled; les autres options ont priorité”.

Les optimisations de -funsafe-math peuvent améliorer la vitesse si vous ne vous souciez pas de l’ordre des opérandes, mais cela peut entraîner une perte de précision si l’ordre des opérandes est important, ainsi que des résultats NAN et INF.