160 votes

Les nombres à virgule flottante sont-ils cohérents dans C #? Peuvent-ils être?

Non, ce n'est pas un autre "Pourquoi est - (1/3.0)x3 != 1.0" question.

J'ai lu à propos de floating points beaucoup ces derniers temps; plus précisément, comment le même calcul peut donner des résultats différents sur des architectures différentes ou des paramètres d'optimisation.

C'est un problème pour les jeux vidéo qui stockent des replays, ou peer-to-peer en réseau (par opposition au serveur-client), qui s'appuient sur tous les clients de générer exactement les mêmes résultats à chaque fois qu'ils exécuter le programme - un petit écart dans un calcul flottant peut conduire à une drastique de jeu différents-de l'état sur des ordinateurs différents (ou même sur la même machine!)

Ce qui se passe, même parmi les processeurs qui "suivent" la norme IEEE-754, principalement parce que certains processeurs (à savoir x86) l'utilisation de double précision étendue. Qui est, ils utilisent 80 bits des registres de faire tous les calculs, puis tronquer à 64 ou 32 bits, conduisant à différentes arrondissant les résultats que les machines qui utilisent 64 ou 32 bits pour les calculs.

J'ai vu plusieurs solutions à ce problème en ligne, mais tous pour C++, C#:

  • Désactiver le double extended-mode précision (de sorte que tous double le calcul de la norme IEEE-754 64-bits) à l'aide d' _controlfp_s (Windows), _FPU_SETCW (Linux?), ou fpsetprec (BSD).
  • Toujours exécuter le même compilateur avec les mêmes paramètres d'optimisation, et d'exiger que tous les utilisateurs ont la même architecture de PROCESSEUR (pas de cross-play). Parce que mon "compilateur" est en fait le JIT, ce qui peut optimiser différemment à chaque fois, le programme est exécuté, je ne pense pas que ce soit possible.
  • L'utilisation de la virgule fixe de l'arithmétique, et d'éviter float et double tout à fait. decimal serait de travailler à cette fin, mais serait beaucoup plus lent, et aucun de l' System.Math bibliothèque de fonctions de soutien.

Donc, est-ce encore un problème en C#? Que faire si je ne l'intention de soutenir l'Windows (pas Mono)?

Si elle l'est, est-il un moyen de forcer le programme à exécuter à la normale en double précision?

Si non, existe-il des bibliothèques qui permettrait de garder des calculs en virgule flottante-elle cohérente?

56voto

CodesInChaos Points 60274

Je ne connais pas de façon de façon de faire normale floating points déterministe .net. La Gigue est autorisé à créer un code qui se comporte différemment sur les différentes plates-formes(ou entre les différentes versions de .net). Donc, en utilisant la normale floats dans déterministe .net code n'est pas possible.

Les solutions de contournement, j'ai réfléchi:

  1. Mettre en œuvre FixedPoint32 en C#. Si ce n'est pas trop dur(j'ai une moitié terminé la mise en œuvre) de la très petite plage de valeurs en fait ennuyeux à utiliser. Vous devez être prudent en tout temps afin de vous ni de débordement, ni perdre trop de précision. En fin de compte j'ai trouvé ce pas plus facile que d'utiliser des entiers directement.
  2. Mettre en œuvre FixedPoint64 en C#. J'ai trouvé ça plutôt difficile à faire. Pour certaines opérations intermédiaires entiers de 128 bits serait utile. Mais .net n'offre pas un tel type.
  3. Mettre en œuvre une coutume 32 bits de point flottant. L'absence d'un BitScanReverse intrinsèque provoque quelques désagréments lors de la mise en œuvre de cette. Mais actuellement, je pense que c'est la voie la plus prometteuse.
  4. Utilisez le code natif pour les opérations mathématiques. Entraîne la surcharge d'un délégué d'appel sur chaque opération mathématique.

Je viens juste de commencer une mise en œuvre de logiciels 32 bits à virgule flottante en mathématiques. Il peut le faire sur 70million ajouts/multiplications par seconde sur mon 2.66 GHz i3. https://github.com/CodesInChaos/SoftFloat . Bien évidemment, il est encore très incomplet et le buggy.

29voto

svick Points 81772

La spécification C # (§ 4.1.6 Types de virgule flottante) permet spécifiquement d'effectuer des calculs en virgule flottante avec une précision supérieure à celle du résultat. Donc, non, je ne pense pas que vous pouvez rendre ces calculs déterministes directement dans .Net. D'autres ont suggéré diverses solutions de contournement pour pouvoir les essayer.

16voto

Peter O. Points 9967

La page suivante peut être utile dans le cas où vous avez besoin absolu de la portabilité de ces opérations. Il traite de logiciel pour tester les implémentations de la norme IEEE 754, y compris les logiciels d'émulation des opérations en virgule flottante. La plupart de l'information est probablement spécifique à C ou C++, cependant.

http://www.math.utah.edu/~beebe/logiciel/ieee/

Une remarque sur un point fixe

Binaire point fixe de numéros qui peuvent aussi bien fonctionner comme un substitut à virgule flottante, comme en témoignent les quatre opérations arithmétiques de base:

  • L'Addition et la soustraction sont triviales. Ils fonctionnent de la même façon que les nombres entiers. Juste d'ajouter ou de soustraire!
  • Pour multiplier deux nombres de point, de multiplier les deux nombres puis shift de droite le nombre de fractions de bits.
  • Pour diviser deux nombres de point, de décaler le dividende à gauche le nombre défini de la fraction de bits, puis divisez-le par le diviseur.
  • Le chapitre quatre de ce papier a des conseils supplémentaires sur la mise en œuvre binaire fixe les nombres à virgule.

Binaire point fixe de numéros qui peuvent être mis en œuvre sur tout type de données entier tel que int, long, et BigInteger, et la non-conformes CLS types uint et ulong.

Comme suggéré dans une autre réponse, vous pouvez utiliser les tables de recherche, où chaque élément du tableau est un binaire fixe nombre de point, pour aider à mettre en œuvre les fonctions complexes telles que sinus, cosinus, racine carrée, et ainsi de suite. Si la table de recherche est moins précis que le nombre de point fixe, il est suggéré à la ronde de l'entrée de l'ajout de la moitié de la granularité de la table à l'entrée:

// Assume each number has a 12 bit fractional part. (1/4096)
// Each entry in the lookup table corresponds to a fixed point number
//  with an 8-bit fractional part (1/256)
input+=(1<<3); // Add 2^3 for rounding purposes
input>>=4; // Shift right by 4 (to get 8-bit fractional part)
// --- clamp or restrict input here --
// Look up value.
return lookupTable[input];

9voto

Jonathan Dickinson Points 4655

Est-ce un problème pour le C#?

Oui. Différentes architectures sont le moindre de vos soucis, de différentes fréquences d'images etc. peut conduire à des écarts en raison d'inexactitudes dans flotter des représentations, même si elles sont les mêmes erreurs (par exemple même de l'architecture, à l'exception d'un ralentissement de la GPU sur une seule machine).

Puis-je utiliser le Système.Décimal?

Il n'y a aucune raison que vous ne pouvez pas, cependant, c'est le chien lent.

Est-il un moyen de forcer le programme à exécuter en double précision?

Oui. L'hôte de la CLR runtime vous-même; et la compilation dans tous les nessecary appels/flags (qui modifient le comportement de l'arithmétique à virgule flottante) dans l'application C++ avant d'appeler CorBindToRuntimeEx.

Existe-il des bibliothèques qui permettrait de maintenir des calculs en virgule flottante-elle cohérente?

Pas que je sache.

Est-il une autre façon de résoudre ce problème?

J'ai abordé ce problème avant, l'idée est d'utiliser QNumbers. Ils sont une forme de nombres réels qui sont à point fixe; mais pas de point fixe en base 10 (décimal) - plutôt en base 2 (binaire); de ce fait, la mathématique des primitives sur eux (add, sub, mul, div) sont beaucoup plus rapides que les naïfs en base 10 points fixes; surtout si l' n est le même pour les deux valeurs (ce qui, dans votre cas, il serait). En outre, parce qu'ils font partie intégrante ils ont bien défini les résultats sur chaque plate-forme.

Gardez à l'esprit que le framerate peut affecter ceux-ci, mais il n'est pas mauvais et il est difficile de corriger à l'aide de points de synchronisation.

Puis-je utiliser plus de fonctions mathématiques avec QNumbers?

Oui, aller-retour, une virgule pour ce faire. En outre, vous devriez vraiment être à l'aide de tables de recherche pour le trigonométriques (sin, cos) fonctions; comme ceux-ci peuvent vraiment donner des résultats différents sur différentes plates - formes-et si vous le code correctement, ils peuvent utiliser des QNumbers directement.

6voto

Nathan Whitehead Points 1095

Selon un aspect un peu ancien MSDN entrée de blog de l'équipe de ne pas utiliser SSE/SSE2 pour la virgule flottante, c'est tout x87. Parce que, comme vous l'avez mentionné que vous avez à vous soucier des modes et des drapeaux, et en C# qui n'est pas possible de contrôler. Donc l'utilisation normale d'opérations en virgule flottante ne garantit pas exactement le même résultat sur chaque machine de votre programme.

Pour atteindre une précision de reproductibilité de la double précision, vous allez avoir à faire des logiciels de virgule flottante (ou point fixe) de l'émulation. Je ne sais pas de bibliothèques visual C# pour ce faire.

Selon les opérations dont vous avez besoin, vous pourriez être en mesure de s'en tirer avec précision unique. Voici l'idée:

  • stocker toutes les valeurs dont vous vous souciez en simple précision
  • pour effectuer une opération:
    • développez entrées à double précision
    • faire l'opération en double précision
    • convertir le résultat en arrière à simple précision

Le gros problème avec les x87 est que les calculs peuvent se faire en 53 bits ou 64 bits de précision en fonction de la précision de l'indicateur et si le registre renversé à la mémoire. Mais pour de nombreuses opérations, en effectuant l'opération à haute précision et arrondi vers le bas de la précision sera de garantir la bonne réponse, ce qui implique que la réponse sera garanti d'être le même sur tous les systèmes. Si vous obtenir le plus de précision n'a pas d'importance, puisque vous avez suffisamment de précision pour garantir le droit de réponse dans les deux cas.

Des opérations qui doivent travailler dans ce schéma: addition, soustraction, multiplication, division, racine carrée. Des choses comme le sin, exp, etc. ne fonctionne pas (les résultats sont généralement match, mais il n'y a pas de garantie). "Quand est double arrondissement inoffensif?" ACM de Référence (payé reg. req.)

Espérons que cette aide!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X