4

I'm working on a real time application that deals with money in different currencies and exchange rates using BigDecimal, however I'm facing some serious performance issues and I want to change the underlying representation.

I've read again and again that a good and fast way of representing money in Java is by storing cents (or whatever the required precision is) using a long. As one of the comments pointed out, there are some libs with wrappers that do just that, such as FastMoney from JavaMoney.

Two questions here.

  1. Is it always safe to store money as a long (or inside a wrapper) and keep everything else (like exchange rates) as doubles? In other words, won't I run into basically the same issues as having everything in doubles if I do Math.round(money * rate) (money being cents and rate being a double)?

  2. FastMoney and many other libs only support operations between them and primitive types. How am I supposed to get an accurate representation of let's say the return of an investment if I can't do profit.divide(investment) (both being FastMoney). I guess the idea is I convert both to doubles and then divide them, but that would be inaccurate right?

Sergey Ponomarev
  • 2,947
  • 1
  • 33
  • 43
Mattx
  • 65
  • 6
  • 6
    Why would you multiply currency amounts together? Do you often calculated "$50 times $2,75"? Storing the amount as cents only requires you to do formatting for display, you don't need any libraries for it. – Kayaman May 28 '18 at 07:18
  • 5
    @Kayaman you mean you *don't* use square dollars? – Andy Turner May 28 '18 at 07:21
  • 1
    @AndyTurner only if I'm buying square eggs. – Kayaman May 28 '18 at 07:23
  • I prefer [Triganic Pu](http://hitchhikers.wikia.com/wiki/Triganic_Pu) myself. But you need big pockets. – Stephen C May 28 '18 at 07:24
  • 50 USD times 0.85 EUR/USD = 42.5 EUR, both 50 and 0.85 would be stored using longs. – Mattx May 28 '18 at 07:24
  • 1
    `0.85` is not a currency amount, it's an exchange rate. Why do you want to store that in a long? – Kayaman May 28 '18 at 07:32
  • Do you mean currency amounts cannot be stored in double but exchange rates can safely be stored in doubles? I have everything in BigDecimals now, maybe that was overkill. – Mattx May 28 '18 at 07:55
  • You need to understand the problem you're trying to fix or avoid. The issue is "how to store currency", but you seem to treat it as "I want to start using fixed point instead of floating point for everything". Those are very different things. – Kayaman May 28 '18 at 08:39
  • I reworded the question as requested by some users. @Kayaman, explain further please. – Mattx May 28 '18 at 09:21

1 Answers1

4

The functionality you are looking for is already implemented in the JavaMoney Library.

It has a FastMoney class that does long arithmetic which is exactly what you have asked for.

For New Java Developers - Why long and not double?

Floating point arithmetic in Java leads to some unexpected errors in precision due to their implementation. Hence it is not recommended in financial calculations.

Also note that this is different from the precision loss in long arithmetic calculations which is due to the fractional portion not being stored in the long. This can be prevented during implementation by moving the fractional portion to another long (e.g. 1.05 -> 1 dollar and 5 cents).

References

  1. A Quick Tutorial

  2. Project Website

Dhruvan Ganesh
  • 1,502
  • 1
  • 18
  • 30
  • I think that's good enough answer. You still need to read through the specifications by yourself in order to use it.Just mention that there is a Money class which is based on big decimal and FastMoney which is based on longs. – Veselin Davidov May 28 '18 at 07:25
  • I'm not sure I want to use a wrapper (ie, FastMoney), that would create thousands and thousands of unnecessary short lived objects. Any benefit apart from type safety? – Mattx May 28 '18 at 07:32
  • @Mattx Yes: you put the responsibility of the code on the library instead of you. Also, you standardize your code, so that the operations are hidden from the developer because it can be the source of issues. These days, the cost of short-lived objects is usually negligible. Try profiling with actual micro-benchmarks to see if the issue really is in there or not. "Premature optimization is the root of all evil." – Olivier Grégoire May 28 '18 at 07:50
  • I was planning to use longs and an interface with static methods to hide the logic of the operations, that code I pasted is just an example, in reality it would become Decimal.multiply(decimal1, decimal2). Anyway, I'll benchmark FastMoney and compare it with BigDecimals and long/double as a baseline. – Mattx May 28 '18 at 08:02
  • On a quick test computing (1.0+0.25)*0.5 one million times using FastMoney (for representing 1.0 and 0.25) it proved to be slower than BigDecimal for the same task. Not sure why. – Mattx May 28 '18 at 18:57