web-dev-qa-db-fra.com

Quand devrais-je utiliser double au lieu de décimal?

Je peux nommer trois avantages à utiliser double (ou float) au lieu de decimal:

  1. Utilise moins de mémoire.
  2. Plus rapide car les opérations mathématiques en virgule flottante sont prises en charge de manière native par les processeurs.
  3. Peut représenter une plus grande gamme de nombres.

Mais ces avantages ne semblent s’appliquer qu’aux opérations intensives de calcul, telles que celles trouvées dans les logiciels de modélisation. Bien entendu, les doublons ne doivent pas être utilisés lorsqu'une précision est requise, telle que des calculs financiers. Existe-t-il des raisons pratiques de choisir double (ou float) au lieu de decimal dans des applications "normales"?

Edité à ajouter: Merci pour toutes les bonnes réponses, j'ai appris d'eux.

Une autre question: quelques personnes ont fait valoir que les doublons peuvent représenter plus précisément des nombres réels. Une fois déclarés, je penserais qu’ils les représentent généralement avec plus de précision. Mais est-ce vrai d'affirmer que la précision peut diminuer (parfois considérablement) lorsque des opérations en virgule flottante sont effectuées?

255
Jamie Ide

Je pense que vous avez très bien résumé les avantages. Vous manquez cependant un point. Le type decimal est seulement plus précis pour représenter des nombres de base 10 (par exemple, ceux utilisés dans les calculs monétaires/financiers). En général, le type double va offrir au moins une précision aussi grande (quelqu'un me corrige si je me trompe) et une vitesse nettement supérieure pour les nombres réels arbitraires. La conclusion est simple: pour déterminer lequel utiliser, utilisez toujours double sauf si vous avez besoin du base 10 précision que decimal offre.

Modifier:

En ce qui concerne votre question supplémentaire sur la diminution de la précision des nombres à virgule flottante après les opérations, il s'agit d'un problème légèrement plus subtil. En effet, la précision (j’utilise le terme de manière interchangeable pour l’exactitude ici) diminuera progressivement après chaque opération. Ceci est dû à deux raisons:

  1. le fait que certains nombres (le plus évidemment des nombres décimaux) ne peuvent pas être vraiment représentés sous forme de virgule flottante
  2. des erreurs d'arrondi se produisent, comme si vous faisiez le calcul à la main. Cela dépend beaucoup du contexte (combien d'opérations que vous effectuez) si ces erreurs sont suffisamment significatives pour mériter une longue réflexion.

Dans tous les cas, si vous souhaitez comparer deux nombres à virgule flottante qui devraient théoriquement être équivalents (mais ont été calculés différemment), vous devez autoriser un certain degré de tolérance (de quelle valeur varie, mais est généralement très faible). .

Pour un aperçu plus détaillé des cas particuliers dans lesquels des erreurs de précision peuvent être introduites, voir la section Précision du article de Wikipedia . Enfin, si vous souhaitez une discussion sérieuse en profondeur (et mathématique) des nombres/opérations en virgule flottante au niveau de la machine, essayez de lire l'article souvent cité - Ce que tout informaticien devrait savoir À propos de l'arithmétique en virgule flottante .

298
Noldorin

Vous semblez avoir les avantages d'utiliser un type à virgule flottante. J'ai tendance à concevoir des décimales dans tous les cas, et je me fie à un profileur pour me faire savoir si les opérations sur la décimale sont à l'origine de goulots d'étranglement ou de ralentissements. Dans ces cas-là, je vais "doubler" ou "flotter", mais uniquement en interne, et essayer avec précaution de gérer la perte de précision en limitant le nombre de chiffres significatifs dans l'opération mathématique exécutée.

En général, si votre valeur est transitoire (non réutilisée), vous pouvez utiliser un type à virgule flottante. Le véritable problème des types à virgule flottante concerne les trois scénarios suivants.

  1. Vous agrégez des valeurs à virgule flottante (auquel cas les erreurs de précision sont composées)
  2. Vous construisez des valeurs basées sur la valeur à virgule flottante (par exemple, dans un algorithme récursif)
  3. Vous faites des mathématiques avec un très grand nombre de chiffres significatifs (par exemple, 123456789.1 * .000000000000000987654321)

[~ # ~] éditer [~ # ~]

Selon le documentation de référence sur les nombres décimaux C # :

Le mot clé décimal désigne un type de données 128 bits. Comparé aux types à virgule flottante, le type décimal a une précision plus grande et une plage plus petite, ce qui le rend approprié pour les calculs financiers et monétaires.

Donc, pour clarifier ma déclaration ci-dessus:

J'ai tendance à concevoir des décimales dans tous les cas, et je me fie à un profileur pour me faire savoir si les opérations sur la décimale sont à l'origine de goulots d'étranglement ou de ralentissements.

Je n'ai jamais travaillé que dans des industries où les décimales sont favorables. Si vous travaillez sur des moteurs graphiques ou graphiques, il est probablement beaucoup plus avantageux de concevoir un type à virgule flottante (float ou double).

Le nombre décimal n’est pas infiniment précis (il est impossible de représenter la précision infinie pour une non-intégrale dans un type de données primitif), mais il est bien plus précis que double:

  • décimal = 28-29 chiffres significatifs
  • double = 15-16 chiffres significatifs
  • float = 7 chiffres significatifs

EDIT 2

En réponse au commentaire de Konrad Rudolph , le point 1 (ci-dessus) est définitivement correct. L'agrégation de l'imprécision est en effet composée. Voir le code ci-dessous pour un exemple:

private const float THREE_FIFTHS = 3f / 5f;
private const int ONE_MILLION = 1000000;

public static void Main(string[] args)
{
    Console.WriteLine("Three Fifths: {0}", THREE_FIFTHS.ToString("F10"));
    float asSingle = 0f;
    double asDouble = 0d;
    decimal asDecimal = 0M;

    for (int i = 0; i < ONE_MILLION; i++)
    {
        asSingle += THREE_FIFTHS;
        asDouble += THREE_FIFTHS;
        asDecimal += (decimal) THREE_FIFTHS;
    }
    Console.WriteLine("Six Hundred Thousand: {0:F10}", THREE_FIFTHS * ONE_MILLION);
    Console.WriteLine("Single: {0}", asSingle.ToString("F10"));
    Console.WriteLine("Double: {0}", asDouble.ToString("F10"));
    Console.WriteLine("Decimal: {0}", asDecimal.ToString("F10"));
    Console.ReadLine();
}

Cela génère les éléments suivants:

Three Fifths: 0.6000000000
Six Hundred Thousand: 600000.0000000000
Single: 599093.4000000000
Double: 599999.9999886850
Decimal: 600000.0000000000

Comme vous pouvez le constater, même si nous ajoutons à partir de la même constante de source, les résultats du double sont moins précis (bien que nous arrondirions probablement correctement) et le flottant est beaucoup moins précis, au point où il a été réduit à seulement deux chiffres significatifs.

56
Michael Meadows

Utilisez décimal pour les valeurs de base 10, par exemple. calculs financiers, comme d'autres l'ont suggéré.

Mais double est généralement plus précis pour des valeurs calculées arbitraires.

Par exemple, si vous souhaitez calculer le poids de chaque ligne d'un portefeuille, utilisez double car le résultat totalise presque 100%.

Dans l'exemple suivant, doubleResult est plus proche de 1 que decimalResult:

// Add one third + one third + one third with decimal
decimal decimalValue = 1M / 3M;
decimal decimalResult = decimalValue + decimalValue + decimalValue;
// Add one third + one third + one third with double
double doubleValue = 1D / 3D;
double doubleResult = doubleValue + doubleValue + doubleValue;

Encore une fois, prenons l'exemple d'un portefeuille:

  • La valeur de marché de chaque ligne du portefeuille est une valeur monétaire et serait probablement mieux représentée sous forme décimale.

  • Le poids de chaque ligne du portefeuille (= valeur marchande/SOMME (valeur marchande)) est généralement mieux représenté en double.

24
Joe

Utilisez un double ou un float lorsque vous n'avez pas besoin de précision, par exemple, dans un jeu de plateforme que j'ai écrit, j'ai utilisé un float pour stocker les vitesses du joueur. Évidemment, je n’ai pas besoin de super précision ici car j’ai fini par arrondir à un Int pour dessiner à l’écran.

6
FlySwat

Dans certaines méthodes comptables, envisagez la possibilité d'utiliser des types intégraux à la place ou conjointement. Par exemple, supposons que les règles que vous appliquez exigent que chaque résultat de calcul soit reporté d’au moins 6 décimales et que le résultat final soit arrondi au cent le plus proche.

Un calcul de 1/6 de 100 $ donne 16,6666666666666666 ..., de sorte que la valeur reportée dans une feuille de calcul sera de 16,666667 $. Les doubles et les décimales doivent donner ce résultat avec précision à 6 décimales. Cependant, nous pouvons éviter toute erreur cumulative en reportant le résultat sous la forme d'un entier 16666667. Chaque calcul ultérieur peut être effectué avec la même précision et reporté de la même manière. En reprenant l’exemple, je calcule la taxe de vente au Texas sur ce montant (16666667 * .0825 = 1375000). En ajoutant les deux (c’est une courte feuille de calcul) 1666667 + 1375000 = 18041667. Le déplacement de la virgule décimale nous donne 18,041667, soit 18,04 $.

Bien que ce court exemple ne produise pas d'erreur cumulative utilisant une double ou une décimale, il est assez facile de montrer des cas dans lesquels le simple calcul du double ou de la décimale et le report entraîneraient une erreur significative. Si les règles que vous appliquez requièrent un nombre limité de décimales, stockez chaque valeur sous forme d’entier en multipliant par 10 ^ (nombre requis de décimales), puis divisée par 10 ^ (nombre requis de décimales) pour obtenir le nombre réel. valeur évitera toute erreur cumulative.

Dans les cas où il n’ya pas de fractions de penny (par exemple, un distributeur automatique), il n’ya aucune raison d’utiliser des types non intégraux. Pensez-y simplement en comptant des sous, pas en dollars. J'ai vu du code où chaque calcul ne comportait que des sous, mais l'utilisation de doubles entraînait des erreurs! Integer only math a supprimé le problème. Donc, ma réponse non conventionnelle est, dans la mesure du possible, d’abandonner le double et le décimal.

4
G DeMasters

Si vous avez besoin d'interopérabilité binaire avec d'autres langages ou plates-formes, vous devrez peut-être utiliser float ou double, qui sont standardisées.

3
Will Dean

Remarque: ce message est basé sur les informations des capacités du type décimal de http://csharpindepth.com/Articles/General/Decimal.aspx et sur ma propre interprétation de ce que cela signifie. Je supposerai que Double correspond à la double précision IEEE normale.

Note2: le plus petit et le plus grand dans ce post indiquent la magnitude du nombre.

Avantages de "décimal".

  • "décimal" peut représenter exactement des nombres qui peuvent être écrits sous forme de fractions décimales (suffisamment courtes), double ne peut pas. Ceci est important dans les grands livres financiers et similaire, où il est important que les résultats correspondent exactement à ce que donnerait un calculateur humain.
  • "décimal" a une mantisse beaucoup plus grande que "double". Cela signifie que pour les valeurs comprises dans sa plage normalisée, "décimal" aura une précision beaucoup plus élevée que le double.

Inconvénients de la décimale

  • Il sera beaucoup plus lent (je n’ai pas de points de repère mais j’imagine qu’au moins un ordre de grandeur peut être supérieur), la décimale ne bénéficiera d’aucune accélération matérielle et l’arithmétique nécessitera une multiplication/division par coût relativement coûteuse de 10 ( ce qui est beaucoup plus coûteux que la multiplication et la division par des puissances de 2) pour faire correspondre l'exposant avant addition/soustraction et ramener l'exposant dans la plage après multiplication/division.
  • la décimale débordera plus tôt que la double volonté. le nombre décimal ne peut représenter que des nombres allant jusqu'à ± 296-1 . Par comparaison, le double peut représenter des nombres allant jusqu'à presque ± 21024
  • le nombre décimal débordera plus tôt. Les plus petits nombres représentables en décimal sont ± 10-28 . Par comparaison, le double peut représenter des valeurs inférieures à 2-149 (environ 10-45) si les sous-nombres sont pris en charge et 2-126 (environ 10-38) s’ils ne le sont pas.
  • la décimale occupe deux fois plus de mémoire que le double.

Mon opinion est que vous devriez utiliser par défaut "décimal" pour le travail de l'argent et d'autres cas dans lesquels le calcul humain exact est important et que vous devez utiliser le double comme choix par défaut le reste du temps.

2
plugwash

Choisissez le type en fonction de votre application. Si vous avez besoin de précision, comme dans l'analyse financière, vous avez répondu à votre question. Mais si votre demande peut régler avec une estimation, votre ok avec double.

Votre application nécessite-t-elle un calcul rapide ou aura-t-il tout le temps du monde pour vous donner une réponse? Cela dépend vraiment du type d'application.

Graphique affamé? float ou double suffit. Analyse de données financières, météore frappant une précision de la planète? Ceux qui auraient besoin d'un peu de précision :)

0
Khan

Une valeur double sera sérialisée en notation scientifique par défaut si cette notation est plus courte que l'affichage décimal. (par exemple .00000003 sera 3e-8) Les valeurs décimales ne seront jamais sérialisées en notation scientifique. Lors de la sérialisation pour la consommation par une partie externe, cela peut être une considération.

0
chris klassen

Le nombre décimal a des octets plus larges, double est pris en charge de manière native par le processeur. La décimale étant en base 10, une conversion décimale-à-double est effectuée pendant qu'une décimale est calculée.

For accounting - decimal
For finance - double
For heavy computation - double

N'oubliez pas que .NET CLR ne prend en charge que Math.Pow (double, double). La décimale n'est pas prise en charge.

.NET Framework 4

[SecuritySafeCritical]
public static extern double Pow(double x, double y);
0
Jeson Martajaya

Cela dépend de ce dont vous avez besoin.

Parce que float et double sont des types de données binaires que vous avez certains Les diifcultés et les erreurs dans les nombres arrondis, ainsi par exemple double arrondirait 0.1 à 0.100000001490116, double arrondirait également 1/3 à 0.33333334326441. En termes simples, tous les nombres réels n'ont pas une représentation précise dans les types doubles

Heureusement, C # prend également en charge l'arithmétique décimale à virgule flottante, dans laquelle les nombres sont représentés via le système numérique décimal plutôt que le système binaire. Ainsi, l'arithmétique décimale à virgule flottante ne perd pas en précision lors du stockage et du traitement de nombres à virgule flottante. Cela le rend tout à fait adapté aux calculs nécessitant un niveau élevé de précision.

0
Neil Meyer

Utilisez des points flottants si vous accordez de la valeur à la performance.

0
Mark Brackett