50 votes

Vitesse décimale ou double

J'écris des applications financières dans lesquelles je me bats constamment pour savoir s'il faut utiliser un double ou une décimale.

Tous mes calculs portent sur des nombres qui n'ont pas plus de 5 décimales et qui ne sont pas supérieurs à ~100 000. J'ai l'impression que tous ces nombres peuvent être représentés par des doubles sans erreur d'arrondi, mais je n'en ai jamais été sûr.

Je passerais bien des décimales aux doubles pour l'avantage évident de la rapidité, sauf qu'en fin de compte, j'utilise toujours la méthode ToString pour transmettre les prix aux bourses, et je dois m'assurer qu'elle produit toujours le nombre que j'attends. (89.99 au lieu de 89.99000000001)

Questions :

  1. L'avantage en termes de vitesse est-il vraiment aussi important que les tests naïfs le suggèrent ? (~100 fois)
  2. Existe-t-il un moyen de garantir que la sortie de ToString sera ce que je veux ? Est-ce que cela est assuré par le fait que mon nombre est toujours représentable ?

MISE À JOUR : Je dois traiter ~ 10 milliards de mises à jour de prix avant que mon application puisse fonctionner, et j'ai implémenté la décimale pour le moment pour des raisons évidentes de protection, mais cela prend ~3 heures juste pour s'allumer, les doubles réduiraient considérablement mon temps d'exécution. Existe-t-il un moyen sûr de le faire avec des doubles ?

1 votes

Quel est votre goulot d'étranglement ? Où exactement brûlez-vous tous vos cycles CPU ? Sans mesures solides, il y a de fortes chances que vous vous intéressiez à la mauvaise chose.

4 votes

Étant donné que les dénominateurs décimaux sont des puissances de 10 et que les dénominateurs binaires sont des puissances de 2, même une seule décimale ne peut être représentée sans erreur. Par exemple, 0,1 (un dixième) n'a pas d'équivalent exact en binaire, ce qui est le même principe qu'un tiers n'a pas de représentation exacte en décimal.

83voto

Robert Gamble Points 41984
  1. L'arithmétique à 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 soit en train de changer, voir les commentaires).
  2. Les demandes financières doivent toujours utiliser des nombres décimaux, le nombre d'histoires d'horreur découlant de l'utilisation de la virgule flottante dans les applications financières est infini, vous devriez être en mesure de trouver de nombreux exemples de ce type avec une recherche sur Google.
  3. Bien que l'arithmétique décimale puisse être sensiblement plus lente que l'arithmétique à virgule flottante, à moins que vous ne passiez un temps considérable à traiter des données décimales, l'impact sur votre programme sera probablement négligeable. Comme toujours, faites le profilage approprié avant de commencer à vous inquiéter de la différence.

14 votes

+1 : Évitez l'optimisation prématurée. Mesurez d'abord, optimisez seulement après avoir obtenu des preuves.

1 votes

"aucun matériel largement utilisé..." Les Mainframes d'IBM (toujours remarquablement populaires) ont une décimale matérielle.

4 votes

Les nouvelles machines supportant la nouvelle norme de virgule flottante IEEE 754 auront un support arithmétique décimal dans le matériel. L'IBM Power6 en fait partie, je crois.

24voto

Norman Ramsey Points 115730

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 de savoir s'il peut représenter exactement vos nombres.

Quant à la représentation exacte, vous avez raison d'être prudent, car une fraction décimale exacte comme 1/10 n'a pas de contrepartie binaire exacte. Cependant, si vous savez que vous n'avez besoin que de 5 chiffres décimaux de précision, vous pouvez utiliser à l'échelle arithmétique dans laquelle vous opérez sur des nombres multipliés par 10^5. Ainsi, par exemple, si vous voulez représenter 23,7205 exactement, vous le représentez par 2372050.

Voyons si la précision est suffisante : la double précision vous donne 53 bits de précision. Cela équivaut à une précision de plus de 15 chiffres décimaux. Vous disposez donc de cinq chiffres après la virgule et de dix chiffres avant la virgule, 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 points faibles, mais cela devrait suffire pour vous permettre de commencer.

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

Si vous avez accès à C99, vous pouvez également essayer l'arithmétique des nombres entiers mis à l'échelle en utilisant la fonction int64_t Type d'entier 64 bits. La solution la plus rapide dépend de votre plate-forme matérielle.

0 votes

Quelles sont les limites de la multiplication et de la division ici ?

1 votes

Difficile de croire que cette réponse n'a pas été votée plus haut, car elle est bien meilleure pour le PO, qui cherchait des améliorations de vitesse basées sur l'observation des goulots d'étranglement de la perf. La mise à l'échelle et la réduction de l'échelle est une bonne réponse qui évite complètement les erreurs d'arrondi tout en augmentant la vitesse de 100 fois.

0 votes

C'est ce qu'on appelle "l'arithmétique à virgule fixe". fr.wikipedia.org/wiki/Fixed-point_arithmetic D'autres gains de calcul peuvent être réalisés si vous utilisez des facteurs d'échelle en base 2 plutôt qu'en base 10 comme vous l'avez fait, puis divisez et multipliez par des bits de merde. Le fait que ce gain de performance soit significatif dépend bien sûr de votre algorithme global et du profilage des performances.

18voto

Craig Points 15049

Utilisez toujours les décimales pour tous les calculs financiers, sinon vous serez toujours à la recherche d'erreurs d'arrondi d'un centime.

7 votes

+1 : Ne perdez pas de temps sur les performances, sauf si vous avez la preuve que c'est le paquet mathématique.

7 votes

Ce n'est pas une réponse aux questions de l'OP.

12voto

Jonathan Leffler Points 299946
  1. Oui, l'arithmétique logicielle est vraiment 100 fois plus lente que l'arithmétique matérielle. Ou, du moins, elle est beaucoup plus lente, et un facteur de 100, à un ordre de grandeur près, est à peu près correct. À l'époque où l'on ne pouvait pas supposer que chaque 80386 possédait un coprocesseur à virgule flottante 80387, il y avait aussi la simulation logicielle de la virgule flottante binaire, et c'était lent.
  2. Non, vous vivez dans un monde imaginaire si vous pensez qu'une pure virgule flottante binaire peut représenter exactement tous les nombres décimaux. Les nombres binaires peuvent combiner des moitiés, des quarts, des huitièmes, etc., mais étant donné qu'un décimal exact 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 que le 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 peuvent pas être représentés exactement).

Vous devez donc décider si vous pouvez gérer l'arrondi avant d'appeler ToString() ou si vous devez trouver un autre mécanisme pour gérer l'arrondi de vos résultats lorsqu'ils sont convertis en chaîne de caractères. Vous pouvez également continuer à utiliser l'arithmétique décimale, car elle restera précise et deviendra plus rapide lorsque les machines qui prennent en charge la nouvelle arithmétique décimale IEEE 754 seront commercialisées.

Référence croisée obligatoire : Ce que tout informaticien devrait savoir sur l'arithmétique à virgule flottante . C'est l'une des nombreuses URL possibles.

Informations sur l'arithmétique décimale et la nouvelle norme IEEE 754:2008 sur ce site. Spéléotrove site.

0 votes

1/100 nécessite un diviseur de 5 ainsi qu'une fraction binaire - ou, plus précisément, nécessite deux facteurs de cinq : 1/100 = (1/4)*(1/5)*(1/5). Le 1/4 peut être représenté exactement dans un nombre binaire à virgule flottante, mais pas les facteurs 1/5.

0 votes

@LawrenceDol Ils le sont, bien sûr, et personne n'a dit le contraire. Ce que Jonathan a fait dire, correctement, est qu'ils ne peuvent pas être représentés exactement en binaire. J'espère que votre confusion s'est dissipée au cours des années écoulées.

7voto

Vilx- Points 37939

Juste une question : est-ce VRAIMENT le goulot d'étranglement de votre application ?

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