9
votes

Double mon argent: mon cadre utilise des doubles pour des montants monétaires

J'ai hérité d'un projet dans lequel les montants monétaires utilisent le double type.

Pire, le cadre qu'il utilise et les classes de cadre, utilisent le double de l'argent.

Le cadre Orm gère également la récupération des valeurs de (et de stockage à) la base de données. Dans la base de données, les valeurs d'argent sont le numéro de type (19, 7), mais le cadre Orm le met en doule.

Un peu de contournement entièrement les classes et ormes-cadres, est-ce que je peux faire tout ce que je peux faire pour calculer les valeurs monétaires précisément?

Edit: Ouais, je sais que BigDecimal devrait être utilisé. Le problème est que je suis étroitement lié à un cadre où, par exemple, la classe cadre.commerce.proding.itempriceInfo a des membres double mrawtotalprice; et double mlistprice. Le propre code de la demande de mon entreprise s'étend, E.g, cet élémentPriceInfoclass.

De manière réaliste, je ne peux pas dire à ma compagnie ", ferma deux années de travail, et des centaines de milliers de dollars dépensés, en basant du code sur ce cadre, en raison des erreurs d'arrondies"


4 commentaires

+1 pour titre "double mon argent"


Dans la compréhension de votre situation. Mais: L'argent en espèces et la comptabilité appropriée tend à être importants pour les entreprises. Alors faites des poursuites de négligence. Vous êtes dans un endroit difficile: D'une part, recommandez que la société brûle des années de développement pour résoudre ce qui semblait être un non-problème. D'autre part, invitez de gros problèmes sur la route. Si j'étais vous, je l'ai trafée à ma direction de cette façon, et laissez-les faire appel à l'appel. Vous avez un problème de responsabilité ici, et c'est ce que votre patron est là pour. Documenter toute décision prise.


À ce que @Michael Patrotta a dit, il est correct à 100%. Selon ce que vous faites, ils pourraient être des problèmes de responsabilité énorme de la conduite de cette décision. De plus, ce cadre - est-ce un cadre commercial? Pourriez-vous les convaincre de passer à la place de BigDecimal ou y a-t-il une version qui fait? Sinon, vous auriez besoin de diriger la voie de refactorisation incrémentielle, qui est une situation horrible où vous êtes - je ressens pour vous.


Si vous devez aller avec des doubles de toute façon, vérifiez docs.sun.com/source/806 -3568 / ncg_goldberg.html , explication très minutieuse de la façon dont le point flottant fonctionne, vous trouverez des mathématiques là-bas, mais rien d'effrayant.


5 Réponses :


6
votes

Je ne voyais pas que vous avez mentionné refactoring. Je pense que c'est votre meilleure option ici. Au lieu de jeter ensemble des hacks pour que les choses puissent mieux travailler pour le moment, pourquoi ne pas le réparer de la bonne façon?

Voici quelques informations sur double vs bigdecimal . Cet article suggère d'utiliser bigdecimal même s'il est plus lent.


3 commentaires

efficace Java le suggère aussi (2e édition, point 48).


Je devrais espérer que cela suggère bigdecimal ... la vitesse ne signifie pas beaucoup si vos chiffres et calculs sont incorrects à cause de double .


Depuis l'OP, il semble que ne peut pas refacteur comme le cadre (que je suppose qu'il n'a pas le code source pour) utilise FP Arithmétique.



10
votes

Si tolérable, traitez le type monétaire comme intégré. En d'autres termes, si vous travaillez aux États-Unis, suivez les cents au lieu de dollars, si les cents fournissent la granularité dont vous avez besoin. Les doubles peuvent représenter avec précision des entiers jusqu'à une très grande valeur (2 ^ 53) ( Pas d'erreur d'arrondi jusqu'à cette valeur).

Mais vraiment, la bonne chose à faire est de contourner le cadre entièrement et d'utiliser quelque chose de plus raisonnable. C'est une telle erreur amateur pour le cadre à faire - qui sait quoi d'autre se cache?


0 commentaires

1
votes

Eh bien, vous n'avez pas beaucoup d'options en réalité:

Vous pouvez refactoriser le projet d'utiliser par exemple. BigDecimal (ou quelque chose qui convient mieux à ses besoins) pour représenter de l'argent.

Soyez extrêmement prudent pour débordement / débordement et perte de précision, ce qui signifie ajouter des tonnes de chèques et refactore une proportion encore plus grande du système de manière inutile. Sans oublier combien de recherches seraient nécessaires si vous voulez le faire.

Gardez les choses comme elles sont et espérons que personne n'assume (ceci est une blague).

IMHO, la meilleure solution serait de simplement le refroidir. Cela pourrait être un refactoring lourd, mais le mal est déjà fait et je crois que cela devrait être votre meilleure option.

meilleur, Vassil

P.s. Oh et vous pouvez traiter de l'argent comme des entiers (centimes de comptage), mais cela ne sonne pas comme une bonne idée si vous allez avoir des conversions de devises, calculer l'intérêt, etc.


4 commentaires

Perte de précision peut-être, mais "débordement / sousflage" êtes-vous sérieux?


Double débordement pour des valeurs supérieures à 10 ^ 308 et des sous-flux pour des valeurs inférieures à 10 ^ -308. Quelles devises ont cet ordre de tri?


Ok, débordement très improbable, mais vous ne pouvez pas éventuellement savoir quelles opérations seraient exécutées sur ces chiffres et sous le débit étant une possibilité dans certains scénarios.


En outre, l'ingénierie logicielle consiste également à gérer le risque et, si vous pouvez avaler le succès de la performance, vous devez utiliser BigDecimal, simplement parce qu'il est moins sujet aux erreurs. Oh, et la chasse aux bugs arithmétiques du point flottant rivalise uniquement des problèmes de synchronisation dans la méchanceté.



4
votes

Beaucoup de gens suggèrent d'utiliser BigDecimal et si vous ne savez pas utiliser l'arrondi dans votre projet, c'est ce que vous devriez faire.

Si vous savez utiliser correctement l'arrondissement décimal, utilisez le double. Ses nombreuses ordres de grandeur plus rapides, très clairs et plus simples et plus simples et donc moins émis par des erreurs. Si vous utilisez des dollars et des cents (ou avez besoin de deux décimales), vous pouvez obtenir un résultat précis pour les valeurs jusqu'à 70 milliards de dollars. P>

Fondamentalement, vous ne recevrez pas d'erreurs rondes si vous corrigez pour cela en utilisant Approcher l'arrondi. P>

BTW: la pensée des erreurs d'arrondi frappe la terreur au cœur de nombreux développeurs, mais elles ne sont pas erreurs aléatoires fortes> et vous pouvez les gérer assez facilement. P >

Edit: Considérez cet exemple simple d'une erreur d'arrondi. P>

private static long TENS[] = new long[19]; static { 
    TENS[0] = 1; 
    for (int i = 1; i < TENS.length; i++) TENS[i] = 10 * TENS[i - 1]; 
} 

public static double round(double v, int precision) { 
    assert precision >= 0 && precision < TENS.length; 
    double unscaled = v * TENS[precision]; 
    assert unscaled > Long.MIN_VALUE && unscaled < Long.MAX_VALUE; 
    long unscaledLong = (long) (unscaled + (v < 0 ? -0.5 : 0.5)); 
    return (double) unscaledLong / TENS[precision]; 
}


5 commentaires

Je n'ai pas bownvote, mais sans donner de conseils sur la manière de gérer ces erreurs d'arrondi, la réponse n'est tout simplement pas utile.


Si vous avez affaire à de grosses sommes d'argent, cela n'est pas pratique (pensez des millions de dollars et des milliards de dollars ajoutés / soustraits, etc.). De plus, en fonction du matériel, les erreurs d'arrondies pourraient être différentes, ce qui fournirait des points de douleur futurs potentiels et pourrait certainement causer un comportement imprévisible. Si la précision est nécessaire, vous n'utilisez pas de structures de données estimées à virgule flottante (double / flotteur) et concentrez-vous plutôt sur la précision.


Java effectue la même arrondi et la même précision sur toutes les plateformes JSE, vous trouverez peut-être la différence de plates-formes JME, je n'ai aucune expérience de celles-ci.


@Peter Lawrey: Notez que (depuis JDK1.2), ce seulement détient si vous utilisez strictfp ; Sinon, le JVM est libre d'utiliser une unité de quincaillerie appropriée pour accélérer les choses, même si ce matériel ne fonctionne pas de manière identique à 100% aux propres routines FP de Java.


@sleske, a convenu que cela pourrait être dépendant de la plate-forme et du matériel. Je n'ai pas essayé cela avec Javame ou Android par exemple. Je sais que cela fonctionne avec Oracle JVM (qui a tendance à être plus strict) sur les plates-formes x86 / x64. J'ai des tests unitaires pour confirmer cela. Je conseillerais à quiconque représentant de l'argent / les prix d'avoir des tests unitaires pour leurs composants qui vérifieraient que les erreurs d'arrondi sont traitées comme prévu.



0
votes

Je pense que cette situation est au moins grave de sauvetage pour votre code. Vous obtenez la valeur comme un double via le cadre Orm. Vous pouvez ensuite le convertir en BigDecimal en utilisant la valeur statique de la méthode (voir Ici pour pourquoi) avant de faire des mathématiques / calculs sur celui-ci, puis convertissez-le en double uniquement pour le stocker.

Depuis que vous extensiez ces classes de toute façon, vous pouvez ajouter des getters pour votre double valeur qui les obtiennent comme BigDecimal lorsque vous en avez besoin.

Ceci peut ne pas couvrir 100% des cas (je serais particulièrement inquiet de ce que le pilote ORM ou JDBC effectue pour convertir le double dos à un type de numéro), mais c'est tellement mieux que de faire les mathématiques sur les doubles bruts.

Cependant, je suis loin d'être convaincu que cette approche est réellement moins chère pour la société à long terme.


0 commentaires