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

265

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 nativement par les processeurs.
  3. Peut représenter une plus grande gamme de nombres.

Mais ces avantages semblent s'appliquer uniquement aux opérations de calcul intensif, comme celles que l'on trouve dans les logiciels de modélisation. Bien sûr, les doubles ne doivent pas être utilisés lorsque la précision est requise, comme les calculs financiers. Y a-t-il donc des raisons pratiques de choisir double(ou float) plutôt que decimaldans les applications "normales"?

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

Une autre question: quelques personnes ont fait valoir que les doubles peuvent représenter plus précisément les nombres réels. Une fois déclarés, je pense qu'ils les représentent généralement plus précisément. Mais est-ce une affirmation vraie que la précision peut diminuer (parfois de manière significative) lorsque des opérations en virgule flottante sont effectuées?

Jamie Ide
la source
voir aussi stackoverflow.com/questions/2545567/…
Ian Ringrose
5
Cela fait l'objet de votes assez réguliers et j'ai toujours du mal avec ça. Par exemple, je travaille sur une application qui effectue des calculs financiers, donc j'utilise des décimales partout. Mais les fonctions Math et VisualBasic.Financial utilisent le double, donc il y a beaucoup de conversion qui me fait constamment deviner l'utilisation de la décimale.
Jamie Ide
@JamieIde c'est fou que les fonctions financières utilisent le double, l'argent doit toujours être en décimal.
Chris Marisic
@ChrisMarisic Mais que peut faire Jamie Ide en travaillant avec de la merde héritée en utilisant le double? Ensuite, vous devez également utiliser le double sinon les nombreuses conversions entraîneront des erreurs d'arrondi ... pas étonnant qu'il ait mentionné VisualBasic pfffhh .....
Elisabeth
@Elisabeth, j'utiliserais probablement une bibliothèque différente qui prend correctement en charge les décimales. Tout ce que VisualBasic.Financial fournit existe probablement dans plusieurs autres bibliothèques aujourd'hui
Chris Marisic

Réponses:

306

Je pense que vous avez assez bien résumé les avantages. Il vous manque cependant un point. Le decimaltype est seulement plus précis pour représenter les nombres de base 10 (par exemple ceux utilisés dans les calculs monétaires / financiers). En général, le doubletype va offrir au moins une grande précision (quelqu'un me corrige si je me trompe) et une vitesse nettement supérieure pour les nombres réels arbitraires. La conclusion simple est la suivante: lorsque vous envisagez de l'utiliser, utilisez-le toujours à doublemoins que vous n'ayez besoin de la base 10précision qu'il decimaloffre.

Éditer:

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 la précision ici) diminuera régulièrement après chaque opération. Cela est dû à deux raisons:

  1. le fait que certains nombres (le plus évidemment 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 grandement du contexte (du nombre d'opérations que vous effectuez) si ces erreurs sont suffisamment importantes pour justifier une réflexion approfondie.

Dans tous les cas, si vous souhaitez comparer deux nombres à virgule flottante qui devraient en théorie être équivalents (mais qui ont été déterminés à l'aide de calculs différents), vous devez autoriser un certain degré de tolérance (la quantité varie, mais est généralement très petite) .

Pour un aperçu plus détaillé des cas particuliers où des erreurs de précision peuvent être introduites, consultez la section Précision de l'article Wikipedia . Enfin, si vous voulez une discussion sérieuse (et mathématique) sur les nombres / opérations à virgule flottante au niveau de la machine, essayez de lire l'article souvent cité Ce que tout informaticien devrait savoir sur l'arithmétique à virgule flottante .

Noldorin
la source
1
Pouvez-vous fournir un exemple, un nombre de base 10 avec lequel la précision est perdue lors de la conversion en base 2?
Mark Cidade
@Mark: 1.000001 en est un exemple, du moins selon Jon Skeet. (Voir la question 3 de cette page: yoda.arachsys.com/csharp/teasers-answers.html )
Noldorin
25
@Mark: exemple très simple: 0,1 est une fraction périodique en base 2 donc elle ne peut pas être exprimée précisément en a double. Les ordinateurs modernes imprimeront toujours la valeur correcte, mais uniquement parce qu'ils «devinent» le résultat - pas parce qu'il est vraiment exprimé correctement.
Konrad Rudolph
1
Le Decimaltype a une précision de 93 bits dans la mantisse, contre environ 52 pour double. Je souhaite que Microsoft prenne en charge le format IEEE 80 bits, même s'il doit être complété à 16 octets; il aurait permis une plage plus large doubleou Decimal, bien meilleure Decimal, que la prise en charge des opérations transcendantales (par exemple sin (x), log (x), etc.), et une précision qui, bien que pas aussi bonne que Decimalserait bien meilleure que double.
supercat
@charlotte: Si vous lisez mon post complet, vous verrez que cela est expliqué.
Noldorin
59

Vous semblez avoir les avantages d'utiliser un type à virgule flottante. J'ai tendance à concevoir des décimales dans tous les cas, et je compte sur un profileur pour me faire savoir si les opérations sur les décimales provoquent des goulots d'étranglement ou des ralentissements. Dans ces cas, je vais "down cast" pour doubler ou flotter, mais seulement le faire en interne, et essayer soigneusement de gérer la perte de précision en limitant le nombre de chiffres significatifs dans l'opération mathématique effectuée.

En général, si votre valeur est transitoire (non réutilisée), vous pouvez utiliser un type à virgule flottante en toute sécurité. Le vrai problème avec les types à virgule flottante est 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 créez des valeurs basées sur la valeur à virgule flottante (par exemple dans un algorithme récursif)
  3. Vous faites des calculs avec un très grand nombre de chiffres significatifs (par exemple, 123456789.1 * .000000000000000987654321)

ÉDITER

Selon la documentation de référence sur les décimales 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 plus grande précision 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 compte sur un profileur pour me faire savoir si les opérations sur les décimales provoquent des goulots d'étranglement ou des ralentissements.

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

Décimal n'est pas infiniment précis (il est impossible de représenter une précision infinie pour les non-intégrales dans un type de données primitif), mais il est beaucoup 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 , l'élément # 1 (ci-dessus) est définitivement correct. L'agrégation de l'imprécision aggrave en effet. 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 voir, même si nous ajoutons à partir de la même constante de source, les résultats du double sont moins précis (bien que probablement arrondis correctement), et le flottant est beaucoup moins précis, au point où il a été réduit à seulement deux chiffres significatifs.

Michael Meadows
la source
1
Le point 1 est incorrect. Les erreurs de précision / d'arrondi se produisent uniquement dans la coulée, pas dans les calculs. Il est bien sûr exact que la plupart des opérations mathématiques sont instables, multipliant ainsi l'erreur. Mais c'est un autre problème et il en va de même pour tous les types de données de précision limitée, en particulier pour les décimales.
Konrad Rudolph
1
@Konrad Rudolph, voir l'exemple dans "EDIT 2" comme preuve du point que j'essayais de faire dans le point # 1. Souvent, ce problème ne se manifeste pas parce que l'imprécision positive s'équilibre avec l'imprécision négative, et ils disparaissent l'agrégat, mais l'agrégation du même nombre (comme je l'ai fait dans l'exemple) met en évidence le problème.
Michael Meadows
Excellent exemple. Je viens de le montrer à mes développeurs juniors, les enfants étaient stupéfaits.
Machado
Maintenant, pouvez-vous faire la même chose avec 2 / 3rds au lieu de 3 / 5ths ...
gnasher729
1
@ gnasher729, l'utilisation des 2/3 au lieu des 3/5 n'a pas été parfaitement gérée pour les différents types. Fait intéressant, la valeur flottante a produit Single: 667660.400000000000tandis que la valeur décimale a donné Decimal: 666666.7000000000. La valeur flottante est légèrement inférieure à mille sur la valeur correcte.
jhenninger
25

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

Mais le double est généralement plus précis pour les valeurs calculées arbitrairement.

Par exemple, si vous souhaitez calculer le poids de chaque ligne d'un portefeuille, utilisez le double car le résultat totalisera 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;

Reprenons donc l'exemple d'un portfolio:

  • La valeur marchande 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 (= Market Value / SUM (Market Value)) est généralement mieux représenté par le double.

Joe
la source
6

Utilisez un double ou un flotteur lorsque vous n'avez pas besoin de précision, par exemple, dans un jeu de plateforme que j'ai écrit, j'ai utilisé un flotteur pour stocker les vitesses des joueurs. Évidemment, je n'ai pas besoin de super précision ici car je finis par me tourner vers un Int pour dessiner sur l'écran.

FlySwat
la source
3
La précision étant le SEUL avantage des décimales, c'est vrai. Vous ne devriez pas demander quand vous devez utiliser des nombres à virgule flottante sur des décimales. Cela devrait être votre première pensée. La question est alors de savoir quand vous devez utiliser des décimales (et la réponse est ici ... lorsque la précision compte).
Instance Hunter
3
@Daniel Straight, c'est drôle, mais j'ai l'opinion contraire. Je pense que l'utilisation d'un type moins précis en raison de ses caractéristiques de performance équivaut à une préoptimisation. Vous devrez peut-être payer plusieurs fois cette préoptimisation avant de vous rendre compte de ses avantages.
Michael Meadows
3
@Michael Meadows, je peux comprendre cet argument. Il convient toutefois de noter que l'une des principales plaintes concernant l'optimisation prématurée est que les programmeurs n'ont pas tendance à savoir ce qui va être lent. Nous savons sans aucun doute, cependant, que les décimales sont plus lentes que les doubles. Néanmoins, je suppose que dans la plupart des cas, l'amélioration des performances ne sera de toute façon pas visible pour l'utilisateur. Bien sûr, dans la plupart des cas, la précision n'est pas nécessaire non plus. Il h.
Instance Hunter
La virgule flottante décimale est en fait MOINS précise que la virgule flottante binaire utilisant le même nombre de bits. L'avantage de Decimal est de pouvoir représenter exactement des fractions DÉCIMALES comme 0,01 qui sont courantes dans le calcul financier.
dan04
Eh bien, ce n'est pas tout à fait correct :) - dans de nombreux jeux, les nombres à virgule flottante peuvent être indésirables, car ils ne sont pas cohérents. Voir ici
BlueRaja - Danny Pflughoeft
4

Dans certains comptes, envisagez la possibilité d'utiliser des types intégraux à la place ou conjointement. Par exemple, supposons que les règles sous lesquelles vous opérez exigent que chaque résultat de calcul soit reporté avec au moins 6 décimales et le résultat final sera arrondi au centime le plus proche.

Un calcul de 1/6 de 100 $ donne 16,6666666666666666 ..., donc la valeur reportée dans une feuille de calcul sera 16,666667 $. Les doubles et les décimales devraient produire 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 manière similaire. Poursuivant l'exemple, je calcule la taxe de vente du Texas sur ce montant (16666667 * .0825 = 1375000). Ajouter les deux (c'est une courte feuille de calcul) 1666667 + 1375000 = 18041667. Remettre le point décimal en arrière nous donne 18.041667, ou 18.04 $.

Bien que ce court exemple ne produise pas d'erreur cumulative en utilisant le double ou le décimal, il est assez facile de montrer des cas où le simple calcul du double ou du décimal et le report accumuleraient une erreur significative. Si les règles sous lesquelles vous opérez nécessitent un nombre limité de décimales, stockez chaque valeur sous forme d'entier en multipliant par 10 ^ (nombre de décimales requis), puis en divisant par 10 ^ (nombre de décimales requis) pour obtenir le nombre réel La valeur évitera toute erreur cumulative.

Dans les situations où des fractions de cent ne se produisent pas (par exemple, un distributeur automatique), il n'y a aucune raison d'utiliser des types non intégraux. Pensez-y simplement comme comptant des sous, pas des dollars. J'ai vu du code où chaque calcul n'impliquait que des sous entiers, mais l'utilisation du double a conduit à des erreurs! Entier uniquement les mathématiques ont supprimé le problème. Donc, ma réponse non conventionnelle est, lorsque cela est possible, de renoncer à la fois au double et à la décimale.

G DeMasters
la source
3

Si vous devez interagir binaire avec d'autres langages ou plates-formes, vous devrez peut-être utiliser float ou double, qui sont standardisés.

Will Dean
la source
2

Remarque: ce message est basé sur des informations sur les 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 suppose que Double est une double précision IEEE normale.

Note2: les plus petits et les plus grands dans ce post se réfèrent à l'ampleur 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), le double ne peut pas. Ceci est important dans les grands livres financiers et similaires où il est important que les résultats correspondent exactement à ce qu'un humain faisant les calculs donnerait.
  • "décimal" a une mantisse beaucoup plus grande que "double". Cela signifie que pour les valeurs dans sa plage normalisée, "décimal" aura une précision beaucoup plus élevée que le double.

Contre décimal

  • Ce sera beaucoup plus lent (je n'ai pas de repères mais je suppose qu'au moins un ordre de grandeur peut-être plus), la décimale ne bénéficiera d'aucune accélération matérielle et l'arithmétique nécessitera une multiplication / division relativement coûteuse par puissances de 10 ( ce qui est beaucoup plus cher que la multiplication et la division par puissances de 2) pour faire correspondre l'exposant avant l'addition / la soustraction et pour ramener l'exposant dans la plage après la multiplication / la division.
  • la décimale débordera plus tôt que la double volonté. décimal ne peut représenter que des nombres jusqu'à ± 2 96 -1. Par comparaison, le double peut représenter des nombres jusqu'à presque ± 2 1024
  • 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 jusqu'à 2 -149 (environ 10 -45 ) si les nombres subnromaux sont pris en charge et 2 -126 (environ 10 -38 ) s'ils ne le sont pas.
  • décimal occupe deux fois plus de mémoire que le double.

Mon opinion est que vous devez utiliser par défaut "décimal" pour le travail de l'argent et d'autres cas où la correspondance exacte du calcul humain est importante et que vous devez utiliser le double comme choix par défaut le reste du temps.

plugwash
la source
2

Cela dépend de ce dont vous avez besoin.

Parce que float et double sont des types de données binaires que vous avez un certain difficultés et des erreurs dans la manière dont les nombres sont arrondis. Autrement dit, tous les nombres réels n'ont pas une représentation précise en types doubles

Heureusement, C # prend également en charge ce que l'on appelle l'arithmétique décimale à virgule flottante, où 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 sa précision lors du stockage et du traitement des nombres à virgule flottante. Cela le rend extrêmement adapté aux calculs nécessitant un haut niveau de précision.

Neil Meyer
la source
0

Utilisez des virgules flottantes si vous appréciez les performances par rapport à l'exactitude.

Mark Brackett
la source
6
Les nombres décimaux ne sont pas plus corrects, sauf dans certains cas limités qui sont parfois (en aucun cas toujours) importants.
David Thornley
0

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

Votre application a-t-elle besoin d'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é? flotter ou double suffit. Analyse des données financières, météore frappant une planète de précision? Celles-ci auraient besoin d'un peu de précision :)

Khan
la source
8
Les nombres décimaux sont également des estimations. Ils sont conformes aux conventions de l'arithmétique financière, mais il n'y a aucun avantage dans, disons, les calculs impliquant la physique.
David Thornley
0

Decimal a des octets plus larges, le double est nativement pris en charge par le CPU. Decimal est en base 10, donc une conversion décimale en double se produit pendant le calcul d'une décimale.

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

Gardez à l'esprit que .NET CLR ne prend en charge que Math.Pow (double, double). Decimal n'est pas pris en charge.

.NET Framework 4

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

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.

chris klassen
la source