Scala Currency Class

Most of you probably ran into the well-known precision problems with floating point numbers at one time or another. If you haven’t, consider this example:

scala> 1.2 - 1.0
res1: Double = 0.19999999999999996

Oops. Shouldn’t that be 0.2? Yes it should, but since the number 0.2 cannot be represented exactly in binary form, the result is a little bit less. This problem becomes annoying when Floats or Double values are used to represent money. It gets worse when multiplication or division operations magnify the error, as for example in the case of interest calculations. Simply put, you can’t guarantee the exactness of calculations down to the cent when you represent monetary amounts as floating point values. The Java language has a type called BigDecimal which solves this problem. It offers arbitrary scale fixed point arithmetics that allows precise financial calculations. Unfortunately, Java’s BigDecimal class has a rather unwieldy API which is a pain to use. No problem, you may think, because Scala offers a wrapper for BigDecimal that lets you use it like a normal number. That is true, but try this out:

scala> val b: BigDecimal = 0.1
b: BigDecimal =
  0.1000000000000000055511151231257827021181583404541015625

Oops again. This doesn’t look much better. What is more, the BigDecimal wrapper class in Scala has abstracted away the control over rounding behaviour and precision offered by the Java API. Of course, you could follow another approach and use Long values to represent money and scale them to the required precision, say one ten thousands of a Dollar. However, there are a number of problems with this approach. First, the value range is limited by MAX_LONG/scale. Second, you have to do complex formatting every time you print the values. Third, you have to code rounding manually because remainders are discarded in integer arithmetics:

scala> 59L / 10L
res2: Long = 5

With this in mind, I have created a Scala currency type that offers arbitrary precision fixed point arithmetics with an easy-to-use API. It is based on the Java’s BigDecimal type – why reinvent the wheel? Let’s repeat the first arithmetic operation using the currency type:

scala> Currency(1.2) - Currency(1.0)
res3: Currency = 0.20

This time the result is correct. It is correct, because the currency type automatically takes care of rounding. By default, the result is rounded to the second decimal place using the ROUND_HALF_UP rounding mode, which means that 1.555 is rounded to 1.56 and -1.555 is rounded to -1.56. Unlike the rounding for Float and Double values, the rounding is symmetric, which is standard in most financial calculations. If you need a different rounding behaviour – no problem. With the currency type, you have complete control over precision and rounding behaviour.

scala> val c = Currency("1234.56789", 4)
c: Currency = 1234.5679

scala> val d = Currency(0.1, 20)
d: Currency = 0.10000000000000000000

scala> import Currency.RoundingMode._
scala> val e = Currency(22.78, 2, ROUND_DOWN)
e: Currency = 22.78

The first expression creates a currency value with a precision of 1/10000 (= four places right of the decimal point) from a string argument where the fifth decimal place is rounded up in construction. The second expression creates a value of 0.1 (10 cents) with a precision of 10-20 or 20 places after the decimal point. The third expression constructs a value of 22.78 with a special rounding mode that stipulates that values should always be rounded down. The effect can be seen when performing an arithmetic operation:

scala> e * 1.1
res4: Currency = 25.05

scala> val f = Currency(22.78, 2, ROUND_UP)
f: Currency = 22.78

scala> e == f
res5: Boolean = true

scala> f * 1.1
res6: Currency = 25.06

In this example, the first operation e * 1.1 yields 25.05. We create another currency object with the same value, however with a different rounding mode. When performing the same operation on the second value f, the result differs by one cent from the first because it was rounded differently. This means that arithmetic operations with currency values that have different precision and/or rounding modes are not transitive. This is of course intended behaviour. The above examples show simple calculations with non-specific currency values. In addition to the numeric amount, the currency type optionally takes a currency designation in the form of an ISO 4217 three-letter code. For example:

scala> val salary = Currency(4789.90, "USD")
salary: Currency = 4789.90 USD

scala> val bonus = Currency(500, "USD")
bonus: Currency = 500.00 USD

scala> val holidayInEurope = Currency(2400, "EUR")
holidayInEurope: Currency = 2400.00 EUR

scala> salary + bonus
res7: Currency = 5289.90 USD

scala> salary + holidayInEurope
MismatchedCurrencyException

You can add, subtract and compare amounts in the same currency (or of a non-specific currency value), but when you try to add two different currency values, you get an exception. Alternatively, different currencies could be implemented as different subtypes, as suggested in Ch.20, Programming in Scala (Odersky, Spoon, Venners). This has the advantage that currency mismatches can be detected at compile time. However, it complicates the design and makes mapping the currency type to a database rather awkward. I think that the disadvantages outweigh the benefits, so I implemented the currency type as a “monolithic” class that knows about all currencies. With this design, it becomes easy to format currency values for output.

scala> salary.format
res8: String = $4,789.90

scala> import java.util.Locale
scala> holidayInEurope.format(new Locale("de", "DE"))
res9: String = 2.400,00 €

scala> holidayInEurope.format(new Locale("fr", "CH"))
res10: String = € 2'400.00

scala> salary.format("\u00A4 #,##0.0000")
res11: String = $ 4,789.9000

The currency class offers a severalfold overloaded format method that formats currency amounts according to either the default locale or a chosen locale. Alternatively, you can specify a formatting pattern and/or symbols to use, or you can use Java’s DecimalFormat class for formatting. Thus you have total control over the formatted output. The currency class can also verbalise amounts in four languages.

scala> salary.say
res12: String =
  four thousand seven hundred eighty nine Dollars ninety Cents

scala> salary.sayNumber
res13: String =
  four thousand seven hundred eighty nine 90/100

scala> val pesos = Currency("1538.20", "MXN")
pesos: Currency = 1538.20 MXN

scala> pesos.say(new Locale("es"))
res14: String =
  mil cincocientos treinta y ocho Mexican Peso veinte Centavo

scala> pesos.sayNumber(new Locale("es"))
res15: String = mil cincocientos treinta y ocho 20/100

Finally, the currency class provides a number of convenience methods, such as percentage(), setDecimals(), abs(), fraction(), integral(), pow(), symbol(), getAllCurrencies(), etc., and a number of factory methods and conversion methods to simplify programming with currency values.

View Source Code.

Download full source code and API documentation.

Java Currency Conversion Class

Commercial applications that aim to provide multi-currency functionality need a currency converter facility with access to up-to-date foreign exchange rates. This is what the CurrencyConverter class offers. Click here to download the CurrencyConverter.java source code.

static CurrencyConverter getInstance()
  Returns a singleton instance of CurrencyConverter.
double convert(double amount, java.lang.String fromCurrency,
java.lang.String toCurrency)
  Converts a double precision floating point value from one
  currency to another.

long convert(long amount, java.lang.String fromCurrency,
java.lang.String toCurrency)
  Converts a long value from one currency to another.

boolean isAvailable(java.lang.String currency)
  Check whether the exchange rate for a given currency is available.

java.lang.String[] getCurrencies()
  Returns all currencies for which exchange rates are available.

java.util.Date getReferenceDate()
  Get the reference date for the exchange rates as a Java Date.

java.lang.String getCacheFileName()
  Get the name of the fully qualified path name of the
  XML cache file.

void setCacheFileName(java.lang.String cacheFileName)
  Set the location where the XML cache file should be stored.

void clearCache()
  Delete XML cache file and reset internal data structure.

The CurrencyConverter class provides an API for accessing the European Central Bank's (ECB) foreign exchange rates. The published ECB rates contain exchange rates for approx. 35 of the world's major currencies. They are updated daily at 14:15 CET. These rates use EUR as reference currency and are specified with a precision of 1/10000 of the currency unit (one hundredth cent). The convert() method performs currency conversions using either double values or 64-bit long integer values. Long values are preferred in order to avoid problems associated with floating point arithmetics. A local cache file is used for storing exchange rates to reduce network latency. The cache file is updated automatically when new exchange rates become available. It is created/updated the first time a call to convert() is made. By calling the clearCache() method before convert(), a reload of the exchange rates from the ECB server is forced. The CurrencyConverter class is implemented as a simple singleton; it is currently not thread-safe. It should be fairly easy to change it into a Java Bean, or to add code for sub-classing. Update Oct-2009: Manish Dubey has extended the CurrencyConverter class to access exchange rates up to 90 days in the past. With this extension, exchange rates for any currency can be retrieved with a specified date within the last 90 days. You can download the extended version here.