Je peux voir quatre méthodes de base que double
pouvez-vis de vous lorsque vous traitez avec des calculs de devise.
Mantisse Trop Petit
Avec ~15 décimales de précision dans la mantisse, vous allez-vous pour obtenir un mauvais résultat chaque fois que vous infligez avec des montants plus que cela. Si vous êtes suivi des cents, des problèmes commencent à se produire avant 1013 (dix milliards de dollars) de dollars.
Alors que c'est un grand nombre, ce n'est pas que les grandes. Le PIB des états-unis d'environ 18 milliards, qui la dépasse, donc tout ce que traiter avec des pays ou de la même société de taille moyenne pourraient facilement obtenir une réponse incorrecte.
En outre, il ya beaucoup de façons que des quantités beaucoup plus petites pourraient dépasser ce seuil lors du calcul. Vous pourriez être en train de faire une projection de croissance ou un sur un certain nombre d'années, ce qui donne une grande valeur finale. Vous pourriez être en train de faire un "what if" des scénarios d'analyse où les différents paramètres possibles sont examinés et une combinaison des paramètres peut entraîner de très grandes valeurs. Vous travaillez peut-être dans les règles qui permettent à une fraction d'un cent qui pourrait hacher l'autre de deux ordres de grandeur ou plus hors de votre portée, vous mettre à peu près en ligne avec la richesse de simples individus en USD.
Enfin, ne prenons pas l'un de NOUS centrée sur de voir les choses. Quels sont les autres monnaies? La Roupie Indonésienne est vaut à peu près de 13 000 USD, ce qui est un autre de 2 ordres de grandeur vous avez besoin pour suivre les montants en devises dans la monnaie (en supposant qu'aucun "grain de sel"!). Vous êtes presque passer pour des montants qui sont d'intérêt pour le commun des mortels.
Voici un exemple où une prévision de croissance de calcul à partir d'1e9 à 5% ne va pas:
method year amount delta
double 0 $ 1,000,000,000.00
Decimal 0 $ 1,000,000,000.00 (0.0000000000)
double 10 $ 1,628,894,626.78
Decimal 10 $ 1,628,894,626.78 (0.0000004768)
double 20 $ 2,653,297,705.14
Decimal 20 $ 2,653,297,705.14 (0.0000023842)
double 30 $ 4,321,942,375.15
Decimal 30 $ 4,321,942,375.15 (0.0000057220)
double 40 $ 7,039,988,712.12
Decimal 40 $ 7,039,988,712.12 (0.0000123978)
double 50 $ 11,467,399,785.75
Decimal 50 $ 11,467,399,785.75 (0.0000247955)
double 60 $ 18,679,185,894.12
Decimal 60 $ 18,679,185,894.12 (0.0000534058)
double 70 $ 30,426,425,535.51
Decimal 70 $ 30,426,425,535.51 (0.0000915527)
double 80 $ 49,561,441,066.84
Decimal 80 $ 49,561,441,066.84 (0.0001678467)
double 90 $ 80,730,365,049.13
Decimal 90 $ 80,730,365,049.13 (0.0003051758)
double 100 $ 131,501,257,846.30
Decimal 100 $ 131,501,257,846.30 (0.0005645752)
double 110 $ 214,201,692,320.32
Decimal 110 $ 214,201,692,320.32 (0.0010375977)
double 120 $ 348,911,985,667.20
Decimal 120 $ 348,911,985,667.20 (0.0017700195)
double 130 $ 568,340,858,671.56
Decimal 130 $ 568,340,858,671.55 (0.0030517578)
double 140 $ 925,767,370,868.17
Decimal 140 $ 925,767,370,868.17 (0.0053710938)
double 150 $ 1,507,977,496,053.05
Decimal 150 $ 1,507,977,496,053.04 (0.0097656250)
double 160 $ 2,456,336,440,622.11
Decimal 160 $ 2,456,336,440,622.10 (0.0166015625)
double 170 $ 4,001,113,229,686.99
Decimal 170 $ 4,001,113,229,686.96 (0.0288085938)
double 180 $ 6,517,391,840,965.27
Decimal 180 $ 6,517,391,840,965.22 (0.0498046875)
double 190 $ 10,616,144,550,351.47
Decimal 190 $ 10,616,144,550,351.38 (0.0859375000)
Le delta (différence entre double
et BigDecimal
frappe d'abord > 1 cent à l'an 160, autour de 2 milliards de dollars (ce qui pourrait ne pas être tout ce que beaucoup plus de 160 ans à partir de maintenant), et bien sûr ne cesse de s'aggraver.
Bien sûr, l'53 bits de Mantisse dire que la relative erreur de ce type de calcul est probablement très faible (j'espère que vous ne perdez pas votre emploi de plus de 1 cent à 2 milliards de dollars). En effet, l'erreur relative fondamentalement détient relativement stable dans la plupart de l'exemple. Vous pourriez certainement l'organiser si de sorte que vous (par exemple) soustraire deux différents avec une perte de précision dans la mantisse résultant en une arbitrairement grand d'erreur (exercice jusqu'au lecteur).
Évolution Sémantique
Si vous pensez que vous êtes assez intelligent, et réussi à venir avec un arrondi régime qui vous permet d'utiliser double
et les ont testés de manière exhaustive vos méthodes sur votre JVM. Aller de l'avant et de le déployer. Demain ou la semaine prochaine ou à chaque fois qu'il y a de pire pour vous, les résultats de la modifier et de vos astuces pause.
Contrairement à presque tous les autres la langue de base de l'expression, et certainement à la différence de nombre entier ou BigDecimal
de l'arithmétique, par défaut, les résultats de nombre à virgule flottante expressions n'ont pas une seule des normes définies valeur en raison de l' strictfp fonctionnalité. Les plates-formes sont libres d'utiliser, à leur discrétion, une plus grande précision des intermédiaires, ce qui peut entraîner des résultats différents sur un matériel différent, JVM versions, etc. Le résultat, pour les mêmes entrées, peut même varier au moment de l'exécution lorsque la méthode des substitutions à partir interprété de JIT-compilé!
Si vous aviez écrit votre code dans le pré-Java 1.2 jours, vous seriez assez énervé lorsque Java 1.2 soudainement introduit la variable par défaut FP comportement. Vous pourriez être tenté d'utiliser strictfp
partout et j'espère que vous n'avez pas dans la multitude des bugs en rapport - mais sur certaines plates-formes, vous seriez de jeter une grande partie de la performance qui double acheté chez vous en premier lieu.
Il n'y a rien de dire que la JVM spec ne vais pas encore changer à l'avenir, pour accueillir de nouvelles modifications de la FP matériel, ou que la JVM des réalisateurs de ne pas utiliser la corde que le défaut de non-strictfp comportement donne à faire quelque chose de délicat.
Inexact Représentations
Comme Roland l'a souligné dans sa réponse, l'un des principaux problème avec double
, c'est qu'il n'est pas exact des représentations pour certains la plupart des non-valeurs entières. Bien qu'un seul non-valeur exacte comme 0.1
souvent "aller-retour" OK, dans certains scénarios (par exemple, Double.toString(0.1).equals("0.1")
), dès que vous faire des maths sur ces imprécis valeurs de l'erreur peut être composé, et cela peut être irrémédiable.
En particulier, si vous êtes "fermer" pour un arrondissement point, par exemple, ~1.005, vous pourriez obtenir une valeur de 1.00499999... lorsque la valeur réelle est 1.0050000001..., ou vice-versa. Parce que les erreurs vont dans les deux directions, il n'y a pas d'arrondi magie qui peut résoudre ce problème. Il n'y a aucun moyen de savoir si une valeur de 1.004999999... devrait être augmenté ou pas. Votre roundToTwoPlaces()
méthode (un type de double arrondi) ne fonctionne que parce qu'il a traité un cas où 1.0049999 devrait être augmenté, mais il ne sera jamais en mesure de traverser la frontière, par exemple, si les erreurs cumulatives cause 1.0050000000001 pour être transformé en 1.00499999999999 il ne peut pas le réparer.
Vous n'avez pas besoin d'un grand ou un petit nombre de frapper cette. Vous avez seulement besoin de mathématiques, et pour que le résultat de l'automne à proximité de la frontière. Plus de maths que vous faites, plus les déviations possibles de la réalité du résultat, et le plus de chance de cheval sur une frontière.
Comme demandé ici d'une recherche de test qui fait un simple calcul: amount * tax
et arrondi à 2 décimales (c'est à dire, en dollars et en cents). Il existe quelques méthodes d'arrondi dans les il y, celui qui est actuellement utilisé, roundToTwoPlacesB
est un surgonflée version de la vôtre1 (par augmentation du multiplicateur n
dans le premier arrondissement vous faites, il est beaucoup plus sensible à la version originale ne parvient pas tout de suite sur trivial entrées).
Le test crache les échecs qu'il trouve, et ils viennent dans des bouquets. Par exemple, les premiers échecs:
Failed for 1234.57 * 0.5000 = 617.28 vs 617.29
Raw result : 617.2850000000000000000000, Double.toString(): 617.29
Failed for 1234.61 * 0.5000 = 617.30 vs 617.31
Raw result : 617.3050000000000000000000, Double.toString(): 617.31
Failed for 1234.65 * 0.5000 = 617.32 vs 617.33
Raw result : 617.3250000000000000000000, Double.toString(): 617.33
Failed for 1234.69 * 0.5000 = 617.34 vs 617.35
Raw result : 617.3450000000000000000000, Double.toString(): 617.35
Notez que le "résultat brut" (c'est à dire, l'exact résultat non arrondi) est toujours à proximité d'un x.xx5000
limite. Votre méthode d'arrondi se trompe à la fois sur le haut et les bas côtés. Vous ne pouvez pas réparer de façon générique.
Imprécis Calculs
Plusieurs de l' java.lang.Math
méthodes ne nécessitent pas correctement arrondie résultats, mais plutôt de permettre aux erreurs de jusqu'à 2,5 ulp. Certes, vous n'allez probablement pas être en utilisant les fonctions hyperboliques beaucoup avec de la monnaie, mais des fonctions telles que exp()
et pow()
souvent à trouver leur chemin dans la monnaie de calculs et de ces seuls ont une précision de 1 ulp. Si le numéro est déjà "mal" lorsqu'il est retourné.
Cette interagit avec le "Inexact Représentation" de l'émission, puisque ce type d'erreur est beaucoup plus grave que celle de la normale mathématique des opérations qui sont à moins de choisir la meilleure valeur possible à partir du représentable domaine de l' double
. Cela signifie que vous pouvez avoir beaucoup plus d'aller-boundary crossing événements lorsque vous utilisez ces méthodes.