Question

J'écris des applications financières dans lesquelles je lutte constamment contre la décision d'utiliser un double vs en utilisant une décimale.

Tous mes calculs portent sur des nombres ne dépassant pas 5 décimales et ne dépassant pas ~ 100 000. J'ai le sentiment que tout cela peut être représenté comme un doublé de toute façon sans erreur d'arrondi, mais je n'ai jamais été sûr.

Je passerais de décimales à doubler pour l'avantage évident de la vitesse, sauf qu'à la fin de la journée, j'utilise toujours la méthode ToString pour transmettre les prix aux échanges et que je dois m'assurer qu'elle génère toujours le nombre que j'attends. (89.99 au lieu de 89.99000000001)

Questions:

  1. Le gain de vitesse est-il vraiment aussi important que le suggèrent des tests naïfs? (~ 100 fois)
  2. Existe-t-il un moyen de garantir que la sortie de ToString soit ce que je veux? Cela est-il assuré par le fait que mon numéro est toujours représentable?

UPDATE: Je dois traiter environ 10 milliards de mises à jour de prix avant que mon application puisse fonctionner, et je l’implémente maintenant avec décimal pour des raisons évidentes de protection, mais il faut environ 3 heures pour le mettre en marche, doubler réduirait considérablement mon allumer le temps. Y at-il un moyen sûr de le faire avec des doubles?

Était-ce utile?

La solution

  1. L'arithmétique en virgule flottante sera presque toujours beaucoup plus rapide, car elle est directement prise en charge par le matériel. Jusqu'à présent, presque aucun matériel largement utilisé ne supporte l'arithmétique décimale (bien que cela change, voir les commentaires).
  2. Les applications financières doivent toujours utiliser des nombres décimaux. Le nombre d'histoires d'horreur liées à l'utilisation de la virgule flottante dans les applications financières est infini. Vous devriez pouvoir trouver de nombreux exemples de ce type avec une recherche Google.
  3. Bien que l'arithmétique décimale puisse être nettement plus lente que l'arithmétique à virgule flottante, son impact sur votre programme risque d'être négligeable, sauf si vous passez beaucoup de temps à traiter des données décimales. Comme toujours, faites le profilage approprié avant de vous soucier de la différence.

Autres conseils

Il y a deux problèmes séparables ici. L’un consiste à savoir si le double a suffisamment de précision pour contenir tous les bits dont vous avez besoin, et l’autre correspond au point où il peut représenter vos chiffres avec exactitude.

En ce qui concerne la représentation exacte, vous avez raison d’être prudent, car une fraction décimale exacte telle que 1/10 n’a pas de contrepartie binaire exacte. Toutefois, si vous savez que vous n’avez besoin que de 5 chiffres décimaux de précision, vous pouvez utiliser l’arithmétique graduée dans laquelle vous utilisez des nombres multipliés par 10 ^ 5. Ainsi, par exemple, si vous souhaitez représenter 23,7205, vous le représentez exactement par 2372050.

Voyons s’il ya suffisamment de précision: la double précision vous donne une précision de 53 bits. Cela équivaut à plus de 15 chiffres décimaux de précision. Cela vous permettrait donc de placer cinq chiffres après le point décimal et dix chiffres avant le point décimal, ce qui semble suffisant pour votre application.

Je mettrais ce code C dans un fichier .h:

typedef double scaled_int;

#define SCALE_FACTOR 1.0e5  /* number of digits needed after decimal point */

static inline scaled_int adds(scaled_int x, scaled_int y) { return x + y; }
static inline scaled_int muls(scaled_int x, scaled_int y) { return x * y / SCALE_FACTOR; }

static inline scaled_int scaled_of_int(int x) { return (scaled_int) x * SCALE_FACTOR; }
static inline int intpart_of_scaled(scaled_int x) { return floor(x / SCALE_FACTOR); }
static inline int fraction_of_scaled(scaled_int x) { return x - SCALE_FACTOR * intpart_of_scaled(x); }

void fprint_scaled(FILE *out, scaled_int x) {
  fprintf(out, "%d.%05d", intpart_of_scaled(x), fraction_of_scaled(x));
}

Il existe probablement quelques difficultés, mais cela devrait suffire à vous aider à démarrer.

Pas de frais généraux pour l’addition, le coût d’une multiplication ou d’une division par deux.

Si vous avez accès à C99, vous pouvez également essayer l'arithmétique des entiers échelonnés à l'aide du type entier int64_t à 64 bits. Ce qui est plus rapide dépend de votre plate-forme matérielle.

Utilisez toujours Decimal pour vos calculs financiers, sinon vous courrez toujours à la chasse aux erreurs d’arrondi 1 cent.

  1. oui; L'arithmétique logicielle est 100 fois plus lente que le matériel. Ou du moins, c'est beaucoup plus lent, et un facteur de 100, à un ordre de grandeur, est à peu près correct. Dans le mauvais temps, quand on ne pouvait pas supposer que chaque 80386 possédait un coprocesseur à virgule flottante 80387, il existait également une simulation logicielle de la virgule flottante binaire, ce qui était lent.
  2. Non; vous vivez dans un pays imaginaire si vous pensez qu'un point flottant binaire pur peut représenter exactement tous les nombres décimaux. Les nombres binaires peuvent combiner des moitiés, des quarts, des huitièmes, etc., mais puisqu’une décimale exacte de 0,01 nécessite deux facteurs d’un cinquième et un facteur d’un quart (1/100 = (1/4) * (1/5) * (1 / 5)) et puisqu'un cinquième n'a pas de représentation exacte en binaire, vous ne pouvez pas représenter exactement toutes les valeurs décimales avec des valeurs binaires (car 0.01 est un contre-exemple qui ne peut pas être représenté exactement, mais qui est représentatif d'une énorme classe de nombres décimaux qui ne peut pas être représenté exactement).

Donc, vous devez décider si vous pouvez gérer l'arrondi avant d'appeler ToString () ou s'il vous faut trouver un autre mécanisme pour traiter l'arrondi de vos résultats au fur et à mesure qu'ils sont convertis en chaîne. Vous pouvez également continuer à utiliser l’arithmétique décimale, car elle restera précise et accélérera une fois que les machines compatibles prendront en charge la nouvelle arithmétique décimale IEEE 754 dans le matériel.

Références croisées obligatoires: Ce que tout informaticien devrait savoir À propos de l'arithmétique en virgule flottante . C'est l'une des nombreuses URL possibles.

Des informations sur l'arithmétique décimale et la nouvelle norme IEEE 754: 2008 sur ce site Speleotrove .

Utilisez simplement un long et multipliez par une puissance de 10. Une fois que vous avez terminé, divisez par la même puissance de 10.

Les décimales doivent toujours être utilisées pour les calculs financiers. La taille des chiffres n'a pas d'importance.

Le moyen le plus simple pour moi d'expliquer consiste à utiliser du code C #.

double one = 3.05;
double two = 0.05;

System.Console.WriteLine((one + two) == 3.1);

Ce morceau de code affichera False même si 3.1 est égal à 3.1 ...

Même chose ... mais en utilisant le nombre décimal:

decimal one = 3.05m;
decimal two = 0.05m;

System.Console.WriteLine((one + two) == 3.1m);

Ceci imprimera maintenant True !

Si vous souhaitez éviter ce type de problème, je vous recommande de vous en tenir aux décimales.

Je vous renvoie à ma réponse donnée à cette question .

Utilisez une valeur longue, enregistrez la plus petite quantité à suivre et affichez les valeurs en conséquence.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top