Décimal vs double vitesse

J’écris des applications financières dans lesquelles je lutte constamment contre la décision d’utiliser un double contre un nombre décimal.

Tous mes calculs fonctionnent sur des nombres ne dépassant pas 5 décimales et ne dépassant pas ~ 100 000. J’ai l’impression que tout cela peut être représenté comme double sans erreur d’arrondi, mais je n’ai jamais été sûr.

J’irais de l’avant et passerais du décimal au double pour le gain de vitesse évident, sauf qu’en fin de compte, j’utilise toujours la méthode ToSsortingng pour transmettre les prix aux échanges, et je dois m’assurer qu’il envoie toujours le numéro I attendre. (89.99 au lieu de 89.99000000001)

Des questions:

  1. L’avantage de la vitesse est-il vraiment aussi important que le suggèrent les tests naïfs? (~ 100 fois)
  2. Existe-t-il un moyen de garantir que le résultat de ToSsortingng soit ce que je veux? Est-ce assuré par le fait que mon numéro est toujours représentable?

MISE À JOUR: Je dois traiter environ 10 milliards de mises à jour de prix avant que mon application ne puisse fonctionner, et j’ai mis en place des décimales dès maintenant pour des raisons de protection évidentes, mais il faut environ 3 heures pour les activer. . Y a-t-il un moyen sûr de le faire avec des doubles?

    1. L’arithmétique en virgule flottante sera presque toujours beaucoup plus rapide car elle est prise en charge directement 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 résultant de l’utilisation de la virgule flottante dans les applications financières étant illimité, vous devriez être en mesure de trouver de nombreux exemples avec une recherche Google.
    3. Bien que l’arithmétique décimale soit nettement plus lente que l’arithmétique à virgule flottante, à moins que vous ne passiez beaucoup de temps à traiter les données décimales, l’impact sur votre programme risque d’être négligeable. Comme toujours, effectuez le profilage approprié avant de vous inquiéter de la différence.

    Il y a deux problèmes distincts ici. L’une est de savoir si le double a suffisamment de précision pour contenir tous les bits dont vous avez besoin, et l’autre est l’endroit où il peut représenter exactement vos numéros.

    En ce qui concerne la représentation exacte, vous avez raison de faire preuve de prudence, car une fraction décimale exacte telle que 1/10 n’a pas d’homologue binary exact. Cependant, si vous savez que vous n’avez besoin que de 5 chiffres décimaux de précision, vous pouvez utiliser l’arithmétique mise à l’ échelle dans laquelle vous opérez sur des nombres multipliés par 10 ^ 5. Donc, par exemple, si vous voulez représenter 23.7205, vous le représentez exactement comme 2372050.

    Voyons s’il y a assez de précision: la double précision vous donne 53 bits de précision. Cela équivaut à plus de 15 chiffres décimaux de précision. Donc, cela vous donnerait cinq chiffres après le point décimal et 10 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 y a probablement quelques moments difficiles mais cela devrait suffire pour vous lancer.

    Pas de frais supplémentaires pour l’ajout, le coût d’une multiplication ou de la division des doubles.

    Si vous avez access à C99, vous pouvez également essayer l’arithmétique entière à l’échelle en utilisant le type entier 64 bits int64_t . Ce qui est plus rapide dépendra de votre plate-forme matérielle.

    Utilisez toujours Decimal pour tout calcul financier ou vous chercherez toujours des erreurs d’arrondi 1cent.

    1. Oui; l’arithmétique des logiciels est 100 fois plus lente que le matériel. Ou, au moins, il est beaucoup plus lent, et un facteur de 100, donner ou prendre un ordre de grandeur, est à peu près correct. Autrefois, lorsque vous ne pouviez pas supposer que tous les 80386 avaient un coprocesseur 80387 à virgule flottante, vous aviez également une simulation logicielle de virgule flottante binary, et c’était lent.
    2. Non; vous vivez dans une terre imaginaire si vous pensez qu’un pur point binary flottant peut représenter exactement tous les nombres décimaux. Les nombres binarys peuvent combiner les moitiés, les quarts, les huitièmes, etc., mais une décimale exacte de 0,01 nécessite deux facteurs d’un cinquième et d’un facteur d’un quart (1/100 = (1/4) * (1/5) * (1 / 5)) et comme un cinquième n’a pas de représentation exacte en binary, vous ne pouvez pas représenter exactement toutes les valeurs décimales avec des valeurs binarys (car 0.01 est un contre-exemple qui ne peut pas être représenté exactement, mais représente une énorme classe de nombres décimaux). ne peut pas être représenté exactement).

    Donc, vous devez décider si vous pouvez gérer l’arrondi avant d’appeler ToSsortingng () ou si vous devez trouver un autre mécanisme permettant d’arrondir vos résultats lorsqu’ils sont convertis en chaîne. Ou vous pouvez continuer à utiliser l’arithmétique décimale, car celle-ci restra précise, et elle sera plus rapide une fois les machines libérées qui supportent la nouvelle arithmétique décimale IEEE 754 dans le matériel.

    Renvoi obligatoire: ce que chaque informaticien devrait savoir sur l’arithmétique en virgule flottante . C’est l’une des nombreuses URL possibles et conduit à un fichier PDF. Il existe une version HTML chez Sun qui est apparemment une version éditée du même article.

    Informations sur l’arithmétique décimale et la nouvelle norme IEEE 754: 2008 sur ce site Speleotrove (matériel précédemment hébergé chez IBM ).

    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 nombres n’est pas importante.

    La façon la plus simple de m’expliquer est d’utiliser du code C #.

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

    Ce bit de code imprimera False même si 3.1 est égal à 3.1 …

    Même chose … mais en décimal:

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

    Cela va maintenant imprimer True !

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

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

    Utilisez un long, stockez la plus petite quantité dont vous avez besoin pour suivre et affichez les valeurs en conséquence.