¿Cuál es el mejor tipo de datos para usar con dinero en la aplicación Java?¿Cuál es el mejor tipo de datos para usar con dinero en la aplicación Java?
Respuesta
Java tiene Currency
clase que representa los códigos de moneda ISO 4217. BigDecimal
es el mejor tipo para representar valores decimales monetarios.
Joda Money ha proporcionado una biblioteca para representar dinero.
¿Por qué no podemos usar flotador o doble en su lugar? –
@Borat Sagdiyev [Esta es la razón por la cual] (http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems). Además, puede consultar [esto] (https://www.securecoding.cert.org/confluence/display/java/NUM04-J.+Do+not+use+floating-point+numbers+if+precise+computation + es + requerido). –
@Borat: puede hacerlo si sabe lo que está haciendo, vea [este artículo] (http://vanillajava.blogspot.com/2011/08/double-your-money-again.html) por Peter Lawrey. pero parece al menos una gran molestia hacer todo el redondeo en cuanto a usar BigDecimales. –
me gustaría utilizar Joda Money
Es todavía en la versión 0.6, pero se ve muy prometedor
BigDecimal es el mejor tipo de datos que se utilizará para la moneda.
Hay un montón de contenedores para moneda, pero todos usan BigDecimal como el tipo de datos subyacente. No se equivocará con BigDecimal, probablemente utilizando el redondeo BigDecimal.ROUND_HALF_EVEN.
Debe utilizar BigDecimal para representar valores monetarios .Se le permite utilizar una variedad de modos de redondeo y en aplicaciones financieras, el modo de redondeo es a menudo un requisito difícil que incluso puede ser obligatoria por ley .
Un tipo integral que representa el menor valor posible. En otras palabras, su programa debería pensar en centavos, no en dólares/euros.
Esto no debería impedir que la GUI lo traduzca de nuevo a dólares/euros.
Tenga en cuenta que la cantidad de dinero puede desbordar el tamaño de int – eversor
@eversor que necesitaría más de 20 millones de dólares la mayoría de las aplicaciones no necesitarían mucho si lo hacen un tiempo suficiente, ya que ni siquiera nuestros gobiernos manejan suficiente dinero para desbordamiento que –
@ratchetfreak Probablemente es mejor usar un largo entonces. –
BigDecimal se puede utilizar, buena explicación de por qué no usar flotador o doble que puede ser visto aquí: Why not use Double or Float to represent currency?
Me gusta usar Tiny Types que envolvería un doble, BigDecimal o int como lo sugirieron las respuestas anteriores. (Utilizaría un doble a menos que surjan problemas de precisión).
Un tipo pequeño le da seguridad de tipo para que no se confunda una moneda doble con otras dobles.
Si bien me gustan los tipos pequeños, debe * Nunca * usar un doble para almacenar un valor monetario. – orien
Puede usar Money and Currency API (JSR 354). Se espera que esta API forme parte de Java 9. Puede usar esta API en Java 7 y Java 8, siempre que agregue dependencias apropiadas a su proyecto.
Para Java 8, agregue la siguiente implementación de referencia como una dependencia a su pom.xml
:
<dependency>
<groupId>org.javamoney</groupId>
<artifactId>moneta</artifactId>
<version>1.0</version>
</dependency>
Esta dependencia será transitiva añadir javax.money:money-api
como dependencia.
A continuación, puede utilizar la API:
package com.example.money;
import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.is;
import java.util.Locale;
import javax.money.Monetary;
import javax.money.MonetaryAmount;
import javax.money.MonetaryRounding;
import javax.money.format.MonetaryAmountFormat;
import javax.money.format.MonetaryFormats;
import org.junit.Test;
public class MoneyTest {
@Test
public void testMoneyApi() {
MonetaryAmount eurAmount1 = Monetary.getDefaultAmountFactory().setNumber(1.1111).setCurrency("EUR").create();
MonetaryAmount eurAmount2 = Monetary.getDefaultAmountFactory().setNumber(1.1141).setCurrency("EUR").create();
MonetaryAmount eurAmount3 = eurAmount1.add(eurAmount2);
assertThat(eurAmount3.toString(), is("EUR 2.2252"));
MonetaryRounding defaultRounding = Monetary.getDefaultRounding();
MonetaryAmount eurAmount4 = eurAmount3.with(defaultRounding);
assertThat(eurAmount4.toString(), is("EUR 2.23"));
MonetaryAmountFormat germanFormat = MonetaryFormats.getAmountFormat(Locale.GERMAN);
assertThat(germanFormat.format(eurAmount4), is("EUR 2,23"));
}
}
¿Qué pasa con la serialización y el almacenamiento en db? ¿Qué formato debe usarse para enviar por cable? –
Creo que Oracle deformó contratítulos incluyendo Java Money en Java 9. Realmente es una pena. Pero una gran respuesta. Todavía podemos usarlo con Maven – borjab
¿Tiene alguna fuente para que Oracle decida no incluir Java Money en Java 9? – Abdull
he hecho un microanálisis (JMH) para comparar Moneta (moneda de Java JSR 354 implementación) contra BigDecimal en términos de rendimiento.
Sorprendentemente, el rendimiento de BigDecimal parece ser mejor que el de Moneta. he utilizado siguiente config moneta:
org.javamoney.moneta.Money.defaults.precision = 19 org.javamoney.moneta.Money.defaults.roundingMode = HALF_UP
package com.despegar.bookedia.money;
import org.javamoney.moneta.FastMoney;
import org.javamoney.moneta.Money;
import org.openjdk.jmh.annotations.*;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.concurrent.TimeUnit;
@Measurement(batchSize = 5000, iterations = 10, time = 2, timeUnit = TimeUnit.SECONDS)
@Warmup(iterations = 2)
@Threads(value = 1)
@Fork(value = 1)
@State(Scope.Benchmark)
@BenchmarkMode(Mode.Throughput)
public class BigDecimalBenchmark {
private static final Money MONEY_BASE = Money.of(1234567.3444, "EUR");
private static final Money MONEY_SUBSTRACT = Money.of(232323, "EUR");
private static final FastMoney FAST_MONEY_SUBSTRACT = FastMoney.of(232323, "EUR");
private static final FastMoney FAST_MONEY_BASE = FastMoney.of(1234567.3444, "EUR");
MathContext mc = new MathContext(10, RoundingMode.HALF_UP);
@Benchmark
public void bigdecimal_string() {
new BigDecimal("1234567.3444").subtract(new BigDecimal("232323")).multiply(new BigDecimal("3.4"), mc).divide(new BigDecimal("5.456"), mc);
}
@Benchmark
public void bigdecimal_valueOf() {
BigDecimal.valueOf(12345673444L, 4).subtract(BigDecimal.valueOf(232323L)).multiply(BigDecimal.valueOf(34, 1), mc).divide(BigDecimal.valueOf(5456, 3), mc);
}
@Benchmark
public void fastmoney() {
FastMoney.of(1234567.3444, "EUR").subtract(FastMoney.of(232323, "EUR")).multiply(3.4).divide(5.456);
}
@Benchmark
public void money() {
Money.of(1234567.3444, "EUR").subtract(Money.of(232323, "EUR")).multiply(3.4).divide(5.456);
}
@Benchmark
public void money_static(){
MONEY_BASE.subtract(MONEY_SUBSTRACT).multiply(3.4).divide(5.456);
}
@Benchmark
public void fastmoney_static() {
FAST_MONEY_BASE.subtract(FAST_MONEY_SUBSTRACT).multiply(3.4).divide(5.456);
}
}
Resultando en
Benchmark Mode Cnt Score Error Units
BigDecimalBenchmark.bigdecimal_string thrpt 10 479.465 ± 26.821 ops/s
BigDecimalBenchmark.bigdecimal_valueOf thrpt 10 1066.754 ± 40.997 ops/s
BigDecimalBenchmark.fastmoney thrpt 10 83.917 ± 4.612 ops/s
BigDecimalBenchmark.fastmoney_static thrpt 10 504.676 ± 21.642 ops/s
BigDecimalBenchmark.money thrpt 10 59.897 ± 3.061 ops/s
BigDecimalBenchmark.money_static thrpt 10 184.767 ± 7.017 ops/s
favor, no dude en corregirme si me falta algo
Interesante, voy a ejecutar la misma prueba con las últimas novedades en JDK9 – kensai
JSR 354: dinero y Moneda API
JSR 354 proporciona una API para representar, transportar y realizar cálculos integrales con dinero y moneda. Se puede descargar desde este enlace:
JSR 354: Money and Currency API Download
La especificación consta de las siguientes cosas:
- Una API para el manejo de correo. gramo. importes monetarios y divisas
- APIs para apoyar las implementaciones intercambiables
- fábricas para la creación de instancias de las clases de implementación
- funcionalidad para los cálculos, la conversión y el formato de los importes monetarios
- API Java para trabajar con el dinero y las monedas, la cual es planeado para ser incluido en Java 9.
- Todas las clases de especificación e interfaces se encuentran en el paquete javax.money. *.
muestra ejemplos de JSR 354: dinero y la moneda API:
Un ejemplo de la creación de un MonetaryAmount e imprimirla a la consola se parece a esto ::
MonetaryAmountFactory<?> amountFactory = Monetary.getDefaultAmountFactory();
MonetaryAmount monetaryAmount = amountFactory.setCurrency(Monetary.getCurrency("EUR")).setNumber(12345.67).create();
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault());
System.out.println(format.format(monetaryAmount));
Cuando mediante la API de implementación de referencia, el código necesario es mucho más simple:
MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault());
System.out.println(format.format(monetaryAmount));
El API también es compatible con los cálculos con MonetaryAmounts:
MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmount otherMonetaryAmount = monetaryAmount.divide(2).add(Money.of(5, "EUR"));
CurrencyUnit y MonetaryAmount
// getting CurrencyUnits by locale
CurrencyUnit yen = MonetaryCurrencies.getCurrency(Locale.JAPAN);
CurrencyUnit canadianDollar = MonetaryCurrencies.getCurrency(Locale.CANADA);
MonetaryAmount tiene varios métodos que permiten el acceso a moneda asignada, la cantidad numérica, su precisión y más:
MonetaryAmount monetaryAmount = Money.of(123.45, euro);
CurrencyUnit currency = monetaryAmount.getCurrency();
NumberValue numberValue = monetaryAmount.getNumber();
int intValue = numberValue.intValue(); // 123
double doubleValue = numberValue.doubleValue(); // 123.45
long fractionDenominator = numberValue.getAmountFractionDenominator(); // 100
long fractionNumerator = numberValue.getAmountFractionNumerator(); // 45
int precision = numberValue.getPrecision(); // 5
// NumberValue extends java.lang.Number.
// So we assign numberValue to a variable of type Number
Number number = numberValue;
MonetaryAmounts pueden redondearse usando un operador de redondeo:
CurrencyUnit usd = MonetaryCurrencies.getCurrency("USD");
MonetaryAmount dollars = Money.of(12.34567, usd);
MonetaryOperator roundingOperator = MonetaryRoundings.getRounding(usd);
MonetaryAmount roundedDollars = dollars.with(roundingOperator); // USD 12.35
Al trabajar con colecciones de MonetaryAmounts, hay algunos buenos métodos de utilidad para filtrar, clasificar y agrupar disponibles.
operacionesList<MonetaryAmount> amounts = new ArrayList<>();
amounts.add(Money.of(2, "EUR"));
amounts.add(Money.of(42, "USD"));
amounts.add(Money.of(7, "USD"));
amounts.add(Money.of(13.37, "JPY"));
amounts.add(Money.of(18, "USD"));
MonetaryAmount personalizada
// A monetary operator that returns 10% of the input MonetaryAmount
// Implemented using Java 8 Lambdas
MonetaryOperator tenPercentOperator = (MonetaryAmount amount) -> {
BigDecimal baseAmount = amount.getNumber().numberValue(BigDecimal.class);
BigDecimal tenPercent = baseAmount.multiply(new BigDecimal("0.1"));
return Money.of(tenPercent, amount.getCurrency());
};
MonetaryAmount dollars = Money.of(12.34567, "USD");
// apply tenPercentOperator to MonetaryAmount
MonetaryAmount tenPercentDollars = dollars.with(tenPercentOperator); // USD 1.234567
Recursos:
Handling money and currencies in Java with JSR 354
Looking into the Java 9 Money and Currency API (JSR 354)
Ver también: JSR 354 - Currency and Money
Todo esto es bueno, pero como Federico sugirió antes, parece más lento que BigDecimal :-)) mal chiste solo entonces, pero lo probaré ahora 1 año después. .. – kensai
Para el caso simple (una moneda) es suficiente Integer
/Long
. mantener el dinero en centavos (...) o centésimas/milésimas de centavos (cualquier precisión que necesita con el divisor fijo)
- 1. ¿Cuál es el mejor tipo de datos para usar con dinero en C#?
- 2. ¿Cuál es la mejor biblioteca matemática para usar con Java?
- 3. ¿Cuál es la mejor base de datos para usar en una aplicación de escritorio Java?
- 4. ¿Qué tipo de datos XML debo usar para moneda/dinero?
- 5. ¿cuál es el mejor alojamiento para la aplicación web java?
- 6. ¿Cuál es la mejor base de datos para usar con un programa Java?
- 7. ¿Cuál es la mejor manera de usar SOAP con Ruby?
- 8. ¿Cuál es la mejor manera de almacenar un valor monetario en la base de datos?
- 9. ¿Cuál es el mejor enlace de OpenGL para Java?
- 10. ¿Cuál es la mejor manera de usar mongoDB con node.js?
- 11. ¿Qué tipo es el mejor para administrar datos binarios en Java?
- 12. ¿Cuál es la mejor alternativa para la serialización de Java?
- 13. ¿Cómo sé cuál es el mejor lugar para usar 'usar'?
- 14. ¿Cuál es el mejor control de navegador web para usar en la aplicación Windows Form?
- 15. ¿Cuál es la "mejor" base de datos para incrustado?
- 16. ¿Cuál es la mejor base de datos de objetos Java?
- 17. Cuál es la mejor aplicación de .dispose()
- 18. ¿Cuál es la mejor manera de conservar datos en una aplicación de escritorio Java?
- 19. ¿Cuál es la mejor forma de usar NLog con MEF?
- 20. ¿Cuál es el tipo de datos para la propiedad de longitud para las matrices de Java?
- 21. ¿Cuál es la mejor forma de interactuar con Ogre (C++) en una aplicación Java?
- 22. ¿Cuál es la mejor estructura de datos para autocompletar texto?
- 23. C: ¿Cuál es el tipo de datos correcto para usar para tamaños de archivo en bytes?
- 24. ¿La mejor manera de guardar datos en una aplicación Java?
- 25. ¿Cuál es el mejor tipo de columna para URL?
- 26. ¿Cuál es el tipo de datos para unix_timestamp (MySQL)?
- 27. ¿Cuál es el mejor tipo de datos para monedas en MySQL?
- 28. ¿Cuál es el tipo de datos "átomo"?
- 29. ¿Cuál es el mejor enfoque para usar JasperReports?
- 30. ¿Cuál es la mejor biblioteca de Java para habilitar la cuadrícula/clúster de su aplicación?
Depende de lo que las operaciones que se va a hacer. Por favor, ofrezca más información. – eversor
@eversor ¿Puede darme una descripción de qué tipo de datos se debe usar para diferentes operaciones? – questborn
Estoy haciendo cálculos que requieren que represente centavos con precisión. – questborn