J'essayais de créer ma propre classe pour les devises en utilisant des positions longues, mais apparemment, je devrais plutôt utiliser BigDecimal
. Quelqu'un pourrait-il m'aider à démarrer? Quelle serait la meilleure façon d'utiliser BigDecimal
s pour les devises en dollars, comme le faire au moins mais pas plus de 2 décimales pour les cents, etc. L'API pour BigDecimal
est énorme et je ne sais pas quelle méthode utiliser. De plus, BigDecimal
a une meilleure précision, mais est-ce que tout n'est pas perdu s'il passe par double
? Si je fais un nouveau BigDecimal(24.99)
, en quoi cela diffère-t-il de l’utilisation d’un double
? Ou devrais-je utiliser le constructeur qui utilise une String
à la place?
Voici quelques astuces:
BigDecimal
pour les calculs si vous avez besoin de la précision qu'il offre (les valeurs Money en ont souvent besoin).NumberFormat
pour l’affichage. Cette classe s’occupera des problèmes de localisation pour les montants en différentes devises. Cependant, il ne prendra que des primitives; Par conséquent, si vous pouvez accepter le petit changement d’exactitude dû à la transformation en une variable double
, vous pouvez utiliser cette classe.NumberFormat
, utilisez la méthode scale()
sur l'instance BigDecimal
pour définir la précision et la méthode d'arrondi.PS: Au cas où vous vous le demanderiez, BigDecimal
est toujours meilleur que double
, lorsque vous devez représenter des valeurs monétaires dans Java .
PPS:
Création d'instances BigDecimal
C’est assez simple puisque BigDecimal
fournit aux constructeurs des objets primitifs , et String
. Vous pouvez utiliser ceux-ci, de préférence celui prenant l'objet String
. Par exemple,
BigDecimal modelVal = new BigDecimal("24.455");
BigDecimal displayVal = modelVal.setScale(2, RoundingMode.HALF_EVEN);
Affichage de BigDecimal
instances
Vous pouvez utiliser les appels de méthode setMinimumFractionDigits
et setMaximumFractionDigits
pour limiter la quantité de données affichées.
NumberFormat usdCostFormat = NumberFormat.getCurrencyInstance(Locale.US);
usdCostFormat.setMinimumFractionDigits( 1 );
usdCostFormat.setMaximumFractionDigits( 2 );
System.out.println( usdCostFormat.format(displayVal.doubleValue()) );
Je recommanderais un peu de recherche sur le modèle de l'argent. Martin Fowler dans son livre Analysis pattern a couvert cela plus en détail.
public class Money {
private static final Currency USD = Currency.getInstance("USD");
private static final RoundingMode DEFAULT_ROUNDING = RoundingMode.HALF_EVEN;
private final BigDecimal amount;
private final Currency currency;
public static Money dollars(BigDecimal amount) {
return new Money(amount, USD);
}
Money(BigDecimal amount, Currency currency) {
this(amount, currency, DEFAULT_ROUNDING);
}
Money(BigDecimal amount, Currency currency, RoundingMode rounding) {
this.currency = currency;
this.amount = amount.setScale(currency.getDefaultFractionDigits(), rounding);
}
public BigDecimal getAmount() {
return amount;
}
public Currency getCurrency() {
return currency;
}
@Override
public String toString() {
return getCurrency().getSymbol() + " " + getAmount();
}
public String toString(Locale locale) {
return getCurrency().getSymbol(locale) + " " + getAmount();
}
}
Venir à l'utilisation:
Vous représenteriez toutes les sommes en utilisant un objet Money
, par opposition à BigDecimal
. Si vous représentez de l’argent sous forme de grande décimale, vous aurez la possibilité de formater l’argent partout où vous l’aurez. Imaginez si la norme d'affichage change. Vous devrez faire les modifications partout. Au lieu d'utiliser le modèle Money
, vous centralisez le formatage de l'argent en un seul endroit.
Money price = Money.dollars(38.28);
System.out.println(price);
Ou attendez JSR-354 . L'API Java Money et Currency bientôt disponible!
1) Si vous êtes limité à la précision double
, une des raisons d'utiliser BigDecimal
s est de réaliser des opérations avec les BigDecimal
s créées à partir de double
s.
2) La variable BigDecimal
consiste en une valeur non calibrée d'entiers de précision arbitraire et une échelle d'entiers non négative de 32 bits, tandis que le double enveloppe une valeur du type primitif double
dans un objet. Un objet de type Double
contient un seul champ dont le type est double
3) Cela ne devrait faire aucune différence
Vous ne devriez avoir aucune difficulté avec le $ et la précision. Une façon de le faire est d'utiliser System.out.printf
Les types numériques primitifs sont utiles pour stocker des valeurs uniques en mémoire. Toutefois, lorsqu’il s’agit de calculs utilisant des types double et float, il ya un problème d’arrondi. Cela se produit car la représentation de la mémoire ne correspond pas exactement à la valeur. Par exemple, une valeur double est supposée prendre 64 bits, mais Java n'utilise pas la totalité des 64 bits. Il ne stocke que ce qu'il pense des parties importantes du nombre. Ainsi, vous pouvez arriver à de mauvaises valeurs lorsque vous ajoutez des valeurs du type float ou double.
S'il vous plaît voir un clip court https://youtu.be/EXxUSz9x7BM
Utilisez BigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP)
lorsque vous souhaitez arrondir aux 2 décimales les centimes. Soyez conscient des erreurs d'arrondi lorsque vous effectuez des calculs. Vous devez être cohérent lorsque vous arrondissez la valeur monétaire. Arrondissez bien à la fin une fois tous les calculs ou appliquez l’arrondissement à chaque valeur avant d’effectuer un calcul. Lequel utiliser dépend des besoins de votre entreprise, mais en général, je pense que l'arrondir à la fin semble me sembler plus judicieux.
Utilisez un String
lorsque vous construisez BigDecimal
pour la valeur monétaire. Si vous utilisez double
, il y aura une fin de virgule flottante à la fin. Cela est dû à l'architecture de l'ordinateur concernant la façon dont les valeurs double
/float
sont représentées au format binaire.
Il existe un exemple détaillé de la procédure à suivre sur javapractices.com. Voir en particulier la classe Money
, conçue pour simplifier les calculs monétaires plutôt que d'utiliser BigDecimal
directement.
La conception de cette classe Money
a pour but de rendre les expressions plus naturelles. Par exemple:
if ( amount.lt(hundred) ) {
cost = amount.times(price);
}
L'outil WEB4J a une classe similaire, appelée Decimal
, qui est un peu plus raffinée que la classe Money
.
NumberFormat.getNumberInstance(Java.util.Locale.US).format(num);
Je serais radical. Pas de BigDecimal.
Voici un excellent article https://lemnik.wordpress.com/2011/03/25/bigdecimal-and-your-money/
Des idées d'ici.
import Java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
testConstructors();
testEqualsAndCompare();
testArithmetic();
}
private static void testEqualsAndCompare() {
final BigDecimal zero = new BigDecimal("0.0");
final BigDecimal zerozero = new BigDecimal("0.00");
boolean zerosAreEqual = zero.equals(zerozero);
boolean zerosAreEqual2 = zerozero.equals(zero);
System.out.println("zerosAreEqual: " + zerosAreEqual + " " + zerosAreEqual2);
int zerosCompare = zero.compareTo(zerozero);
int zerosCompare2 = zerozero.compareTo(zero);
System.out.println("zerosCompare: " + zerosCompare + " " + zerosCompare2);
}
private static void testArithmetic() {
try {
BigDecimal value = new BigDecimal(1);
value = value.divide(new BigDecimal(3));
System.out.println(value);
} catch (ArithmeticException e) {
System.out.println("Failed to devide. " + e.getMessage());
}
}
private static void testConstructors() {
double doubleValue = 35.7;
BigDecimal fromDouble = new BigDecimal(doubleValue);
BigDecimal fromString = new BigDecimal("35.7");
boolean decimalsEqual = fromDouble.equals(fromString);
boolean decimalsEqual2 = fromString.equals(fromDouble);
System.out.println("From double: " + fromDouble);
System.out.println("decimalsEqual: " + decimalsEqual + " " + decimalsEqual2);
}
}
Il imprime
From double: 35.7000000000000028421709430404007434844970703125
decimalsEqual: false false
zerosAreEqual: false false
zerosCompare: 0 0
Failed to devide. Non-terminating decimal expansion; no exact representable decimal result.
Que diriez-vous de stocker BigDecimal dans une base de données? Enfer, ça stocke aussi comme une double valeur ??? Au moins, si j'utilise mongoDb sans configuration avancée, il stockera BigDecimal.TEN
sous le nom 1E1
.
Je suis venu avec une chaîne d’utilisation unique pour stocker BigDecimal en Java en tant que String dans la base de données. Vous disposez d'une validation, par exemple @NotNull
, @Min(10)
, etc. Vous pouvez ensuite utiliser un déclencheur lors de la mise à jour ou de l'enregistrement pour vérifier si la chaîne actuelle est un nombre dont vous avez besoin. Il n'y a pas de déclencheur pour mongo cependant . Existe-t-il une méthode intégrée pour les appels de fonction de déclencheur Mongodb?
Il y a un inconvénient avec lequel je m'amuse - BigDecimal en tant que String dans la définition de Swagger
J'ai besoin de générer de la fanfaronnade, alors notre équipe frontale comprend que je leur passe un nombre présenté comme une chaîne. DateTime par exemple présenté sous forme de chaîne.
Il existe une autre solution intéressante que j'ai lue dans l'article ci-dessus ........ Utilisez long pour stocker des nombres précis.
Une valeur longue standard peut stocker la valeur actuelle de la dette nationale des États-Unis (en cents, et non en dollars) 6477 fois sans aucun débordement. Quoi de plus: c’est un type entier, pas un point flottant. Cela rend le travail plus facile et précis, et garantit un comportement.
https://stackoverflow.com/a/27978223/4587961
Peut-être que dans le futur, MongoDb ajoutera un support pour BigDecimal . https://jira.mongodb.org/browse/SERVER-1393 3.3.8 semble l'avoir fait.
C'est un exemple de la deuxième approche. Utilisez la mise à l'échelle . http://www.technology-ebay.de/the-teams/mobile-de/blog/mapping-bigdecimals-with-morphia-for-mongodb.html