Representación de valores monetarios en Java [cerrado]


94

Entiendo que BigDecimal es la mejor práctica recomendada para representar valores monetarios en Java. ¿Que usas? ¿Hay alguna biblioteca mejor que prefieras usar en su lugar?


4
Eche un vistazo a JSR 354
yegor256

1
Aquí hay una clase de moneda que puede copiar y extender: java-articles.info/articles/?p=254
Gilbert Le Blanc

También vea la implementación de referencia de JSR-354 github.com/JavaMoney/jsr354-ri
venció el

Respuestas:


81

BigDecimaltodo el camino. He oído hablar de algunas personas que crean sus propias clases Casho Moneyque encapsulan un valor en efectivo con la moneda, pero bajo la piel sigue siendo un BigDecimal, probablemente con BigDecimal.ROUND_HALF_EVENredondeo.

Editar: como Don menciona en su respuesta , hay proyectos de código abierto como timeandmoney , y aunque los aplaudo por tratar de evitar que los desarrolladores tengan que reinventar la rueda, simplemente no tengo suficiente confianza en una biblioteca pre-alfa para usar en un entorno de producción. Además, si excavas debajo del capó, verás que BigDecimaltambién lo usan .


4
+1. Hemos decidido agregar una clase de contenedor que también consume una moneda. Esto resulta útil al representar valores monetarios en tablas.
Daniel Hiller

1
sí, ese es un enfoque bastante común y tiene mucho sentido. Una advertencia a esto es cuando tienes que lidiar con el yen japonés, ya que no tienen una denominación de moneda menor como los centavos, por lo que necesita sus propias reglas de redondeo.
Nueve lados

3
@ninesided da un gran ejemplo de por qué lanzar el tuyo propio es una mala respuesta. "Ah, y por cierto, no funciona para $ CURRENCY_X". Esa es una buena señal de que tampoco funciona para muchas otras monedas.
James Moore

1
@JamesMoore No estoy de acuerdo con que "rodar el tuyo" sea un mal enfoque, solo debes ser consciente de las posibles limitaciones del enfoque que elijas, de ahí la razón por la que lo menciono. Es trivial implementar diferentes reglas de redondeo por moneda, pero si su sistema solo necesita operar en USD o EUR, entonces no es necesario diseñar demasiado las cosas.
Nueve lados

1
Eche un vistazo a stackoverflow.com/questions/5134237/… por solo una razón por la que BigDecimal es un problema. La contabilidad planetaria es solo un pantano de casos especiales, y tratar de barrerlos a todos bajo la alfombra de BigDecimal simplemente no funciona.
James Moore



8

Una biblioteca conveniente con la que me encontré anteriormente es la biblioteca Joda-Money . Una de sus implementaciones se basa de hecho en BigDecimal. Se basa en la ISO-4217 especificación para monedas y puede admitir una lista de monedas personalizada (cargada a través de CVS).

Esta biblioteca tiene una pequeña cantidad de archivos que se pueden revisar rápidamente si se necesitan modificaciones. Joda-Money se publica bajo la licencia Apache 2.0.


7

Si solo usa dólares y centavos, usaría un largo (compensado por 2 lugares decimales). Si necesita más detalles, el decimal grande puede ser el camino a seguir.

De cualquier manera, probablemente ampliaría la clase para tener un .toString () que use el formato correcto, y como un lugar para colocar otros métodos que puedan surgir (durante mucho tiempo, multiplicar y dividir saldrá mal si el decimal no es 't ajustado)

Además, si usa definir su propia clase e interfaz, puede reemplazar la implementación a voluntad.


2
Tenga cuidado, incluso mucho tiempo podría ser demasiado corto para mantener la Deuda Federal de EE. UU. En centavos ... si no ahora, en unos pocos años.
Ingo

3
Estoy de acuerdo: cualquier cantidad enorme de dólares (o tal vez si está rastreando dinero en yenes) debería usar BigDecimal, pero incluso entonces consideraría seriamente usar una clase de contenedor para ello. Creo que la mayor parte de la complejidad de la programación proviene de personas que no definen clases pequeñas y simples en torno a colecciones y tipos intrínsecos.
Bill K

3

BigDecimal u otra representación de punto fijo es lo que generalmente se necesita para el dinero.

Las representaciones y cálculos de coma flotante ( Double, Float) son inexactos, lo que da lugar a resultados erróneos.


7
Estrictamente hablando, BigDecimal también es inexacto; simplemente se corresponde mejor con el redondeo decimal al que estamos acostumbrados en la vida diaria y le permite especificar modos de redondeo.
Michael Borgwardt

1
@Michael Borgwardt BigDecimal se diferencia de IEEE FP en que se especifica una escala explícita. Si bien no todas las operaciones son exactas, esto asegura que un conjunto de operaciones y comportamiento sea siempre exacto y la escala sea constante mientras que la escala para IEEE FP disminuye con el valor.

1
¿Qué tiene eso que ver con el dinero? Las organizaciones contables de todo el mundo suelen tener requisitos muy específicos sobre cómo se hacen las matemáticas en su moneda. ¿BigDecimal coincide exactamente con todos y cada uno de estos estándares? ¿Lo hará el año que viene, cuando cambien esos estándares? Y BigDecimal ni siquiera se acerca a especificar reglas de redondeo útiles para las monedas.
James Moore

2

Hay que tener mucho cuidado al tratar con el tiempo y el dinero.

Cuando trabaje con dinero, espero que todos sepan que nunca deben usar un flotador o un doble.

Pero no estoy seguro de BigDecimal.

En la mayoría de los casos, estará bien si solo realiza un seguimiento de los centavos en un int o long. De esta forma, nunca se ocupará de un decimal.

Solo muestra dólares cuando lo imprime. Siempre trabaje con centavos internos usando números enteros. Esto puede ser complicado si necesita dividir o utilizar Math.abs ().

Sin embargo, es posible que le importe medio centavo, o incluso una centésima de centavo. No sé cuál es una buena forma de hacer esto. Es posible que solo tenga que lidiar con una milésima de centavos y usar un largo. O tal vez te verás obligado a usar BigDecimal

Leería mucho más sobre esto, pero ignoraría a todos los que comienzan a hablar de usar un flotador o un doble para representar dinero. Solo están buscando problemas.

Siento que mi consejo no está completo, así que por favor, reflexione más sobre él. ¡Estás tratando con tipos peligrosos!


2
¿Por qué tendría que ser "obligado" a utilizar BigDecimal? ¿De qué no estás seguro? Es claramente superior a trabajar con centavos, ya que le permite especificar explícitamente modos de redondeo.
Michael Borgwardt

1
@MichaelBorgwardt: sí, le permite especificar un pequeño subconjunto de los modos de redondeo que necesita para las monedas. ¿Entonces? (Sugerencia: el redondeo de las monedas lo deciden, por lo general, las organizaciones nacionales de contabilidad. Están encantadas de lanzar en casos especiales extraños. Consulte stackoverflow.com/questions/5134237/… para conocer una de las muchas razones entretenidas por las que el redondeo BigDecimal es completamente inútil aquí.)
James Moore

@James: ¿Cómo es exactamente "inútil"? ¿Cómo sería más difícil implementar estos casos especiales con BigDecimal que con otra cosa?
Michael Borgwardt

1
Bien, completamente inútil es demasiado fuerte. En la clase complicada que abstrae la moneda, las reglas de redondeo de BigDecimal son probablemente útiles en algunos casos específicos para construir un subconjunto de las formas en que ocurre el redondeo de moneda. Pero el caso general es que las reglas de redondeo para las monedas requieren mecanismos que están sujetos a cambios con el tiempo (dado que las agencias de contabilidad humana son las que elaboran las reglas y son libres de cambiarlas). La pregunta no es sobre euros (o lo que reemplace al euro el próximo mes ...), o dólares en 2011, se trata de moneda, así que tienes que lidiar con mucha complejidad desagradable.
James Moore

2

Crear una clase de Money es el camino a seguir. Usando BigDecimal (o incluso un int) debajo. Luego, use la clase de moneda para definir la convención de redondeo.

Desafortunadamente, sin el operador sobrecargado, Java hace que sea bastante desagradable crear tipos tan básicos.


2

Hay una biblioteca mejor, tiempo y dinero . En mi opinión, es muy superior a las bibliotecas proporcionadas por el JDK para representar estos 2 conceptos.


3
Esta respuesta se publicó hace tres años. Hoy en día, el proyecto timeandmoney sigue siendo pre-alfa según ese enlace.
James Moore

1
@JamesMoore Buena decisión. La respuesta tiene ahora 7 años y el proyecto aún no es estable.
Navin

1

Definitivamente no BigDecimal. Hay tantas reglas especiales para el redondeo y la presentación de las que debe preocuparse.

Martin Fowler recomienda la implementación de una clase Money dedicada para representar los montos de moneda, y que también implementa reglas para la conversión de moneda.


6
y el tipo de datos subyacente de su clase Money? BigDecimal.
Nueve lados

1
Eso no es cierto. Podría usar Integer en la clase monetaria, que es lo que hace Martin. He hecho esto muchas veces.
egervari

Sin embargo, la recomendación es correcta; Los cálculos que involucran dinero son un vasto pantano de casos especiales que cambian con el tiempo. BigDecimal puede ser útil como una pequeña parte de la solución, pero ciertamente no es general.
James Moore

1

Oye, aquí hay un artículo muy interesante sobre BigDecimal y un ejemplo ilustrativo de por qué a veces se usa en lugar de dobles. Tutorial de BigDecimal .


0

Puede usar la clase DecimalFormat cuando finalmente muestre un valor de moneda. Proporciona soporte de localización y es bastante extensible.


0

Encapsularía BigDecimal en la clase Money que también tiene una moneda como alguien mencionado anteriormente. Lo importante es que hagas una cantidad extrema de pruebas unitarias y especialmente si trabajas con diferentes monedas. También es una buena idea si agrega un constructor conveniente que tome una cadena o un método de fábrica que haga lo mismo para que pueda escribir sus pruebas de esta manera:

   assertEquals(Money.create("100.0 USD").add("10 GBP"),Money.create("116 USD"));

0

Siempre hay limitaciones y detalles involucrados. Cualquiera que no tenga suficiente experiencia para apreciar los problemas sutiles descritos en el siguiente artículo debe reconsiderar seriamente antes de tratar con datos financieros del mundo real:

http://lemnik.wordpress.com/2011/03/25/bigdecimal-and-your-money

BigDecimal no es la única representación correcta o la única pieza del rompecabezas. Dadas ciertas condiciones, usar una clase Money respaldada por centavos almacenados como un número entero podría ser suficiente y sería mucho más rápido que BigDecimal. Sí, eso implica el uso de dólares como moneda y límites de montos, pero tales restricciones son perfectamente aceptables para muchos casos de uso y todas las monedas tienen casos especiales de redondeo y sub-denominaciones de todos modos, por lo que no existe una solución "universal".


1
Esto parece ser un comentario en otra publicación en lugar de una respuesta real. También es demasiado vitriólico. Intente ser más civilizado en el futuro.
Slater Victoroff
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.