10
votes

Si deux langues suivent IEEE 754, les calculs dans les deux langues entraînent-ils les mêmes réponses?

Je suis en train de convertir un programme de code Scilab en C ++. Une boucle en particulier produise un résultat légèrement différent de celui du code Scilab d'origine (c'est un long morceau de code, donc je ne vais pas l'inclure dans la question, mais je vais faire de mon mieux pour résumer la question ci-dessous).

Le problème est que chaque étape de la boucle utilise des calculs de l'étape précédente. De plus, la différence entre les calculs ne devient apparente autour de la 100 000e itération (sur environ 300 000).

Remarque: Je comparais la sortie de mon programme C ++ avec les sorties de Scilab 5.5.2 à l'aide du "format (25);" commander. Ce qui signifie que je comparais des chiffres importants. J'aimerais également souligner que je comprends comment la précision ne peut être garantie après un certain nombre de bits, mais lisez les sections ci-dessous avant de commenter. Jusqu'à présent, tous les calculs ont été identiques jusqu'à 25 chiffres entre les deux langues.

En tentative d'aller au bas de ce problème, j'ai essayé jusqu'à présent:

  1. examinant le type de données utilisé:

    J'ai réussi à confirmer que Scilab utilise les doubles IEEE 754 (selon la documentation de la langue). De plus, selon Wikipedia, C ++ n'est pas requis pour utiliser IEEE 754 pour les doubles, mais à partir de ce que je peux dire, partout, j'utilise un double en C ++, il correspond parfaitement aux résultats de Scilab.

    1. examiner l'utilisation de fonctions transcendantaux:

      J'ai aussi lu depuis Ce que chaque scientifique informatique devrait savoir sur la flottante Arthmétique Point que IEEE ne nécessite pas de fonctions transcendantaux pour être exactement arrondi. Dans cet esprit, j'ai comparé les résultats de ces fonctions (SIN (), COS (), EXP ()) dans les langues et à nouveau, les résultats semblent être les mêmes (jusqu'à 25 chiffres).

      1. l'utilisation d'autres fonctions et des valeurs prédéfinies:

        J'ai répété les étapes ci-dessus pour l'utilisation de SQRT () et POW (). Ainsi que la valeur de PI (j'utilise m_pi en C ++ et% PI à Scilab). Encore une fois, les résultats étaient les mêmes.

        1. enfin, j'ai réécrit la boucle (très soigneusement) afin de vous assurer que le code est identique entre les deux langues.

          Note: Fait intéressant, j'ai remarqué que pour tous les calculs ci-dessus, les résultats entre les deux langues correspondent plus loin que le résultat réel des calculs (en dehors de l'arithmétique de point flottant). Par exemple:

          valeur du péché (x) à l'aide de wolfram alpha = 0.123456789 .....

          valeur du péché (x) utilisant scilab & c ++ = 0,12345yyyyyy .....

          où, même une fois la valeur calculée à l'aide de Scilab ou C ++, a commencé à différer du résultat réel (de Wolfram). Le résultat de chaque langue correspond toujours mutuellement. Cela me conduit à croire que la plupart des valeurs sont calculées (entre les deux langues) de la même manière. Même s'ils ne sont pas tenus de par IEEE 754.


          Ma pensée initiale a été l'un des trois premiers points ci-dessus sont mis en œuvre différemment entre les deux langues. Mais d'après ce que je peux dire, tout semble produire des résultats identiques.

          est-il possible que même si toutes les entrées de ces boucles sont identiques, les résultats peuvent être différents? Peut-être parce qu'une très petite erreur (passé ce que je peux voir avec 25 chiffres) se produit qui s'accumule au fil du temps? Si oui, comment puis-je faire la réparation de ce problème?


12 commentaires

Pouvez-vous simplement persister les valeurs de point flottant sans conversion ou formatage? Il semble que la bibliothèque de gestion HDF5 de Scilab devrait pouvoir écrire des attributs dans le format "H5T_IEEE_F64LE" (en supposant que votre architecture est petite-Endian). Ensuite, il vous suffit de persister le même format HDF5 à partir de C ++ et vous pouvez les comparer exactement.


"Jusqu'à présent, tous les calculs ont été identiques jusqu'à 25 chiffres". Considérant que l'IEEE double a 15-17 chiffres décimaux de précision, c'est un résultat mieux que moyen.


Ni C ++ ni C # ont même les mêmes résultats que eux-mêmes si vous compilez différemment (par exemple, X87 VS SSE, pour C # qui revient à X86 VS X64).


@ Inutile, je ne suis pas vraiment sûr de ce que vous entendez en persistant les flotteurs. Je vais regarder dans HDF5 mais malheureusement, je ne suis pas en mesure de modifier le code Scilab de quelque manière que ce soit (si cela affecte la sortie). Si c'est ce que vous suggérez.


@harold dans quelle mesure? Signification Si vous deviez exécuter 1 + 1 en C ++, il aurait une même sortie sur la plupart des compilateurs. Est-ce quelque chose qui serait légèrement des calculs seulement? Et puisque je constate que 25 chiffres entre les langues correspondent. Cette erreur pourrait-elle se produire un jour après ces chiffres et affecter encore le programme?


@HAROLD: Vous pouvez contrôler le comportement via des drapeaux de compilateur. Si vous compilez avec / FP: précis , vous obtiendrez des résultats identiques, quelle que soit l'unité de point flottante utilisée. L'effet est qu'une valeur stockée dans un registre FPU de 80bits est copiée en mémoire (l'arrondir ainsi sur 64bits), puis la lecture avant l'opération suivante. Les performances s'effondrent, mais il assure des résultats identiques sur des unités de points flottants (FPU vs. SSE / AVX).


Pourquoi avez-vous même besoin de nombreux chiffres de précision?


@Jesperjuhl Je n'ai pas besoin d'un niveau de précision aussi élevé. J'ai juste besoin que les résultats des deux langues soient identiques. Ainsi, j'ai testé les intrants pour voir à quel point ils étaient similaires afin de déterminer si tel était le problème. Ils viennent juste d'être similaires à 25 chiffres (le plus que je puisse afficher à Scilab).


S'ils sont identiques jusqu'à 25 chiffres et que vous n'avez même pas besoin de cela, pourquoi n'utilisez-vous pas simplement - dire - les 10 premiers chiffres? Et s'ils sont égaux que tout est bon et que vous ignorez simplement le reste et vous déplacez sur ...?


J'échresse la suggestion d'expérimenter avec des drapeaux de compilateur. Certains compilateurs par défaut des paramètres d'optimisation agressifs permettant d'accorder des expressions de point flottant à la ré-arrangement (par exemple de manière équivalente mathématiquement mais non dans l'arithmétique à point variable de précision), peuvent contracter le contrat multiplie et ajoute en FMA ou utiliser des réductions de SIMD. Vérifiez la documentation du compilateur pour le commutateur (ES) qui promettons la plus stricte conformité avec IEEE-754 et d'autres normes pertinentes. Par exemple sur mon compilateur Intel qui est / FP: strict . Vous pouvez perdre beaucoup de performances lorsque vous utilisez un tel drapeau.


Persist = enregistrer sur le disque. Et, écrire des valeurs intermédiaires sur le disque ne doit pas modifier le résultat, sauf si cela n'affecte l'optimisation.


"J'ai comparé les résultats de sin (), cos (), exp () dans les deux langues et les résultats semblent être les mêmes" - cela s'applique à tout Entrées ou juste Quelques entrées ? Par exemple, une technique de mise en oeuvre courante consiste à utiliser sin (x) == x pour petit x, mais exactement comment il faut x être?


4 Réponses :


6
votes

Certaines architectures fournissent la capacité d'utiliser des registres de points flottants de précision étendus (par exemple 80 bits à l'intérieur, contre des valeurs 64 bits en RAM). Donc, il est possible d'obtenir des résultats légèrement différents pour le même calcul, en fonction de la structure des calculs et du niveau d'optimisation utilisé pour compiler le code.


4 commentaires

Donc, même si je devais utiliser exactement les mêmes fonctions (ce qui signifie, copier tout ce que Scilab utilise pour le péché (), COS (), etc. dans C ++), il est possible que les résultats puissent toujours être différents?


Si une langue / la mise en œuvre utilise X87 et l'autre utilise, disons, SSE ou fonctionnant sur SPARC ou POWER ou autre chose, puis oui. Vous n'êtes pas garanti des résultats identiques au dernier bit.


@Jesperjuhl Comment puis-je continuer à déterminer si les deux langues utilisent la même architecture?


S'ils fonctionnent sur le même processeur, ils utilisent la même architecture. Quant à savoir s'ils utilisent la même partie partie de cette architecture - vous devez savoir ce que Scilab utilise, puis déterminez comment rendre votre compilateur C ++ utiliser les mêmes instructions (par exemple, désactiver AVX , désactivation rapide de mathématiques, etc.)



6
votes

Non, le format du système de numérotation ne garantit pas de réponses équivalentes des fonctions dans différentes langues.

fonctions, telles que sin (x) , peut être implémentée de différentes manières, en utilisant la même langue (ainsi que des langues différentes). La fonction sin (x) est un excellent exemple. De nombreuses implémentations utiliseront une table de recherche ou une table d'appartements avec interpolation. Cela a des avantages de vitesse. Cependant, certaines implémentations peuvent utiliser une série Taylor pour évaluer la fonction. Certaines implémentations peuvent utiliser des polynômes pour trouver une approximation étroite.

Avoir le même format numérique est un obstacle à résoudre entre les langues. La mise en œuvre de la fonction est une autre.

N'oubliez pas que vous devez considérer la plate-forme également. Un programme utilisant un processeur de points flottants 80 bits aura des résultats différents d'un programme utilisant une implémentation logicielle à point flottante de 64 bits.


7 commentaires

Comme je l'ai suggéré dans l'un de vos messages précédents, vous devez écrire vos propres fonctions pour fournir plus de cohérence dans les résultats, en particulier entre deux langues différentes. Par exemple, écrivez une fonction sin en Fortran et mettez-la de la même manière en C ++. Cela vous donne une meilleure précision lors de la comparaison des résultats. Sinon, vous comparez des pommes aux oranges.


En fait, c'est ce que j'ai prévu de faire ensuite. Le seul problème est que les résultats doivent être identiques. Par conséquent, je dois utiliser toutes les fonctions Scilab utilise actuellement pour calculer le résultat et je suis de difficulté à les localiser dans le code source.


En ce qui concerne votre réponse, lorsque vous mentionnez avoir le même format numérique. Savez-vous si je le fais déjà ou pas? Je sais que Scilab suit IEEE pour la double précision et je suppose que c ++ est aussi aussi. Est-il possible que c ++ soit juste assez similaire pour apparaître la même chose, mais c'est vraiment une implémentation différente?


Pouvez-vous écrire vos propres fonctions transcendantaux à Scilab? C'est ce que je suggère. Sinon, essayez d'obtenir le code source du compilateur SCILAB ou des fonctions mathématiques.


Enfin, je gère ces deux programmes sur la même plate-forme (c'est-à-dire mon ordinateur personnel). Cela éviterait-il le problème du dernier point de votre réponse?


Non, le code est ce qui compte. Je peux avoir un processeur de points flottant sur mon ordinateur et avoir un programme qui ne l'utilise pas. Vous devez vérifier que toutes les dépendances sont les mêmes entre les deux fonctions. Encore une fois, cela est simplifié lorsque vous écrivez vos propres fonctions dans les deux langues.


@Paulwarnick: Que votre compilateur C ++ implémente ou non la représentation de points flottants IEEE 754 est documenté dans le manuel du compilateur. Il n'existait aucune obligation de mettre en œuvre la langue C ++ d'utiliser une représentation de points flottante donnée.



1
votes

Notez qu'il est possible que la scilab et le C ++ n'utilise pas exactement la même séquence d'instructions, ou celle utilisée par la FPU et l'autre utilise SSE, donc il ne peut donc pas y avoir un moyen de les amener exactement. < P> Comme indiqué par IInpectable, si votre compilateur a _Control87 () ou quelque chose de similaire, vous pouvez l'utiliser pour modifier les paramètres de précision et / ou d'arrondi. Vous pouvez essayer des combinaisons de ceci pour voir si cela a un effet, mais encore une fois, même vous parvenez à obtenir les paramètres identiques pour SCILAB et C ++, les différences dans les séquences d'instructions réelles peuvent être le problème.

http://msdn.microsoft.com/en-us/library/e9b52ceh.aspx

Si SSE est utilisé, je ne sais pas ce que vous ne pouvez pas être ajusté car je ne pense pas que SSE ait un mode de précision de 80 bits.

dans le cas de l'utilisation de FPU En mode 32 bit, et si votre compilateur n'a pas quelque chose comme _Control87, vous pouvez utiliser le code de montage. Si l'assemblage en ligne n'est pas autorisé, vous devez appeler une fonction d'assemblage. Cet exemple est d'un ancien programme de test: xxx



3
votes

Oui, il est possible d'avoir un résultat différent. Il est possible même si vous utilisez exactement le même code source dans le même langage de programmation pour la même plate-forme. Parfois, il suffit d'avoir un autre commutateur de compilateur; Par exemple, -ffastmath conduirait le compilateur à optimiser votre code de vitesse plutôt que de précision, et si votre problème de calcul n'est pas bien conditionné pour commencer, le résultat peut être Différent.

Par exemple, supposons que vous ayez ce code: xxx

Un moyen de calculer cela consiste à effectuer 7 multiplications. Ce serait le comportement par défaut pour la plupart des compilateurs. Cependant, vous voudrez peut-être accélérer cela en spécifiant l'option compilateur -ffastmath et le code résultant n'aurait que 3 multiplications: xxx

le résultat serait Soyez légèrement différent car l'arithmétique de précision finie n'est pas associative, mais suffisamment proche de la plupart des applications et beaucoup plus rapidement. Cependant, si votre calcul n'est pas bien conditionné que la petite erreur peut rapidement être amplifiée dans un grand .


1 commentaires

Excellente réponse aussi. Je commence à voir à quel point il est délicat de réaliser ce que je tente.