230

What data type should you use for money in Java?

Raedwald
  • 46,613
  • 43
  • 151
  • 237
questborn
  • 2,735
  • 2
  • 16
  • 17
  • 2
    It depends of what operations you are going to do. Please offer more information. – eversor Nov 16 '11 at 08:33
  • @eversor Can you give me description of what data type should be used for different operations? – questborn Nov 16 '11 at 08:42
  • 1
    Am doing calculations which requires me to accurately represent cents. – questborn Nov 16 '11 at 08:49
  • Are you able to foretell the bigest amount of money your app will need to handle? And, your calculations, are they going to be simple (aditions etc.) or more complex financial operations? – eversor Nov 16 '11 at 09:31
  • *"It is not currency accepting answers"* – MC Emperor Sep 30 '20 at 14:15
  • I cannot understand why it get closed, while [the question asking about what class should be used in C#](https://stackoverflow.com/questions/693372/what-is-the-best-data-type-to-use-for-money-in-c) is open with 498 up votes. – tsh Mar 13 '23 at 08:44

11 Answers11

177

Java has Currency class that represents the ISO 4217 currency codes. BigDecimal is the best type for representing currency decimal values.

Joda Money has provided a library to represent money.

JodaStephen
  • 60,927
  • 15
  • 95
  • 117
Buhake Sindi
  • 87,898
  • 29
  • 167
  • 228
  • 8
    Why can't we use float or double instead ? – Erran Morad Jun 29 '14 at 06:19
  • 26
    @Borat Sagdiyev [This is the reason why](http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems). Also, you can refer to [this](https://www.securecoding.cert.org/confluence/display/java/NUM04-J.+Do+not+use+floating-point+numbers+if+precise+computation+is+required). – Buhake Sindi Jun 30 '14 at 18:04
  • 3
    @Borat: you can if you know what you're doing, see [this article](http://vanillajava.blogspot.com/2011/08/double-your-money-again.html) by Peter Lawrey. but it seems at least as big a hassle to do all the rounding as to use BigDecimals. – Nathan Hughes Jun 22 '15 at 14:55
  • 69
    "If I had a dime for every time I've seen someone use FLOAT to store currency, I'd have $999.997634" -- Bill Karwin – Collin Krawll Jun 03 '18 at 20:53
47

You can use Money and Currency API (JSR 354). You can use this API in, provided you add appropriate dependencies to your project.

For Java 8, add the following reference implementation as a dependency to your pom.xml:

<dependency>
    <groupId>org.javamoney</groupId>
    <artifactId>moneta</artifactId>
    <version>1.0</version>
</dependency>

This dependency will transitively add javax.money:money-api as a dependency.

You can then use the 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") );
    }
}
Line
  • 1,529
  • 3
  • 18
  • 42
Abdull
  • 26,371
  • 26
  • 130
  • 172
  • 2
    What about serialization and saving into db? What format should be used for sending over wire? – Paweł Szczur Dec 07 '15 at 11:28
  • 1
    I believe that Oracle dediced againts including Java Money in Java 9. Really a shame. But great answer. We still can use it with Maven – borjab Jan 07 '16 at 16:08
  • 4
    Do you have a source for Oracle deciding against including Java Money in Java 9? – Abdull Jan 07 '16 at 16:24
  • 1
    @PawełSzczur It depends what you use for serialization and persistence, but, for example, you can use [jackson-datatype-money](https://github.com/zalando/jackson-datatype-money) for Jackson and [Jadira](http://jadira.sourceforge.net/usertype.core/index.html) for Hibernate. – Dario Seidl Aug 12 '21 at 15:54
  • @Abdull there was talk about Java Money being included in the JDK but that never happened. I don't have a source from Oracle, but there is some info here: https://stackoverflow.com/a/53181704/401712 – Dario Seidl Aug 16 '21 at 12:26
  • is this library deprecated? it hasnt been updated in a long while – Greyshack Aug 19 '23 at 22:04
28

An integral type representing the smallest value possible. In other words your program should think in cents not in dollars/euros.

This should not stop you from having the gui translate it back to dollars/euros.

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
ratchet freak
  • 47,288
  • 5
  • 68
  • 106
  • Bear in mind that the amount of money can overflow the size of int – eversor Nov 16 '11 at 09:17
  • 5
    @eversor that would need over 20 million dollars most apps wouldn't need that much if they do a long will be sufficient as not even our govenrments handle enough mone to overflow that – ratchet freak Nov 16 '11 at 09:19
  • 5
    @ratchetfreak Probably better to use a long then. – le3th4x0rbot May 23 '14 at 07:47
  • 6
    Many banks handle far larger sums of money that $20,000,000 every day. This does not even take account of currencies like yen with large exchange rates to the dollar. Integer types may be best to avoid rounding problems although they get messy with interest and exchange rate calculations. However, depending on the application, you may need a 64-bit integer type. – Alchymist Mar 25 '15 at 12:34
  • Ideally microdollars, actually, as then if you do e.g $10/3 then the rounding error (3333.3 => 3333.0) doesn't affect the final value as much (in this case it doesn't impact the real value at all, although it's dangerous to assume that it never will). This is especially important if you're doing a lot of calculations in a row before your user sees the result, as the rounding errors will compound. – Chris Browne Jul 01 '18 at 09:50
  • Don't forget to consider developed country where they have so many zeroes for money – stuckedunderflow Jan 11 '19 at 03:16
  • 2022 here. We now have cryptocurrencies that do not have any atomic lower bound. Good luck, fellow doomed programmer. – Hubert Grzeskowiak May 02 '22 at 13:16
17

BigDecimal can be used, good explanation of why to not use Float or Double can be seen here: Why not use Double or Float to represent currency?

Community
  • 1
  • 1
Phil Parsons
  • 2,857
  • 22
  • 15
16

JSR 354: Money and Currency API

JSR 354 provides an API for representing, transporting, and performing comprehensive calculations with Money and Currency. You can download it from this link:

JSR 354: Money and Currency API Download

The specification consists of the following things:

  1. An API for handling e. g. monetary amounts and currencies
  2. APIs to support interchangeable implementations
  3. Factories for creating instances of the implementation classes
  4. Functionality for calculations, conversion and formatting of monetary amounts
  5. Java API for working with Money and Currencies, which is planned to be included in Java 9.
  6. All specification classes and interfaces are located in the javax.money.* package.

Sample Examples of JSR 354: Money and Currency API:

An example of creating a MonetaryAmount and printing it to the console looks like this::

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));

When using the reference implementation API, the necessary code is much simpler:

MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault());
System.out.println(format.format(monetaryAmount));

The API also supports calculations with MonetaryAmounts:

MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmount otherMonetaryAmount = monetaryAmount.divide(2).add(Money.of(5, "EUR"));

CurrencyUnit and MonetaryAmount

// getting CurrencyUnits by locale
CurrencyUnit yen = MonetaryCurrencies.getCurrency(Locale.JAPAN);
CurrencyUnit canadianDollar = MonetaryCurrencies.getCurrency(Locale.CANADA);

MonetaryAmount has various methods that allow accessing the assigned currency, the numeric amount, its precision and more:

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 can be rounded using a rounding operator:

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

When working with collections of MonetaryAmounts, some nice utility methods for filtering, sorting and grouping are available.

List<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"));

Custom MonetaryAmount operations

// 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

Resources:

Handling money and currencies in Java with JSR 354

Looking into the Java 9 Money and Currency API (JSR 354)

See Also: JSR 354 - Currency and Money

Aftab
  • 2,863
  • 32
  • 41
  • All this is nice, but as Federico suggested above, it looks slower than BigDecimal :-)) bad joke then only, but I will give it test now 1 year later... – kensai Dec 25 '17 at 05:22
8

I have done a microbenchmark (JMH) to compare Moneta (java currency JSR 354 implementation) against BigDecimal in terms of performance.

Surprisingly, BigDecimal performance seems to be better than moneta's. I have used following moneta config:

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);
    }
}

Resulting in

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

Please feel free to correct me if i'm missing something

7

You should use BigDecimal to represent monetary values .It allows you to use a variety of rounding modes, and in financial applications, the rounding mode is often a hard requirement that may even be mandated by law.

Sandeep Pathak
  • 10,567
  • 8
  • 45
  • 57
6

For simple case (one currency) it'is enough int/long. Keep money in cents (...) or hundredth / thousandth of cents (any precision you need with fixed divider)

Grigory Kislin
  • 16,647
  • 10
  • 125
  • 197
6

I would use Joda Money

It's still at version 0.6 but looks very promising

JodaStephen
  • 60,927
  • 15
  • 95
  • 117
Liviu T.
  • 23,584
  • 10
  • 62
  • 58
4

BigDecimal is the best data type to use for currency.

There are a whole lot of containers for currency, but they all use BigDecimal as the underlying data type. You won't go wrong with BigDecimal, probably using BigDecimal.ROUND_HALF_EVEN rounding.

Anthony Blake
  • 5,328
  • 2
  • 25
  • 24
2

I like using Tiny Types which would wrap either a double, BigDecimal, or int as previous answers have suggested. (I would use a double unless precision problems crop up).

A Tiny Type gives you type safety so you don't confused a double money with other doubles.

Garrett Smith
  • 688
  • 1
  • 7
  • 24
  • 6
    While I too like tiny types, you should *Never* use a double to store a monetary value. – orien Nov 16 '11 at 12:23