Pourquoi le même code produirait-il des résultats numériques différents sur les machines 32 vs 64 bits?

Nous travaillons sur une bibliothèque de routines numériques en C. Nous ne soaps pas encore si nous allons utiliser une simple précision ( float ) ou un double ( double ). Nous avons donc défini le type SP comme alias jusqu’à ce que nous décidions:

 typedef float SP; 

Lorsque nous exécutons nos tests unitaires, ils transmettent tous ma machine (une Ubuntu 64 bits) mais échouent sur la machine de mon collègue (une Ubuntu 32 bits installée par erreur sur une machine 64 bits).

En utilisant la commande en bisect de Git, nous avons trouvé le diff exact qui a commencé à donner des résultats différents entre sa machine et la mienne:

 -typedef double SP; +typedef float SP; 

En d’autres termes, passer de la double précision à la simple précision produit des résultats différents sur nos machines (différence relative d’environ 1e-3 dans les cas les plus défavorables).

Nous sums à peu près certains que nous ne comparons jamais les données non signées aux données négatives signées.

Pourquoi une bibliothèque de routines numériques donnerait-elle des résultats différents sur un système d’exploitation 32 bits et sur un système 64 bits?

CLARIFICATION

Je crains que je 2f3f671 peut-être pas été assez clair: nous avons le commit 2f3f671 Git qui utilise la double précision et où les tests unitaires sont aussi 2f3f671 sur les deux machines. Ensuite, nous avons Git commit 46f2ba , où nous avons changé en simple précision, et ici les tests continuent de passer sur la machine 64 bits, mais pas sur la machine 32 bits.

Vous rencontrez ce qu’on appelle souvent le “bug” de précision excessive x87.

En bref: historiquement, les calculs en virgule flottante (presque) sur les processeurs x86 étaient effectués à l’aide du jeu d’instructions x87, qui opère par défaut sur un type à virgule flottante de 80 bits, mais peut être configuré pour fonctionner en simple ou en double -précision (presque) de quelques bits dans un registre de contrôle.

Si des opérations à simple précision sont effectuées alors que la précision du registre de contrôle x87 est définie sur une précision double ou étendue, les résultats seront alors différents de ceux qui seraient produits si les mêmes opérations étaient effectuées en simple précision (à moins que le compilateur extrêmement prudent et stocke le résultat de chaque calcul et le recharge pour forcer l’arrondi au bon endroit.)

Votre code fonctionnant sur 32 bits utilise l’unité x87 pour le calcul en virgule flottante (apparemment avec le registre de contrôle défini pour la double précision) et rencontre donc le problème décrit ci-dessus. Votre code fonctionnant sur 64 bits utilise les instructions SSE [2,3, …] pour le calcul en virgule flottante, qui fournissent des opérations natives à simple et double précision, et ne portent donc pas une précision excessive. C’est pourquoi vos résultats diffèrent.

Vous pouvez contourner ce -mfpmath=sse (jusqu’à un point) en indiquant à votre compilateur d’utiliser SSE pour le calcul en virgule flottante, même sur 32 bits ( -mfpmath=sse avec GCC). Même dans ce cas, les résultats en bits exacts ne sont pas garantis, car les diverses bibliothèques auxquelles vous vous associez peuvent utiliser x87 ou tout simplement utiliser des algorithmes différents en fonction de l’architecture.

IIRC, la précision de ‘double’ doit uniquement être > = la précision de ‘float’. Ainsi, dans une mise en œuvre, le nombre réel de bits dans “float” et “double” peut être identique, alors que dans l’autre, ils diffèrent. Cela tient probablement à la différence 32 bits / 64 bits des plateformes, mais peut-être pas.