A large number of the world's applications manipulate money values. Here is a convenient, high-performance money structure for the CLR which handles arithmetic operations, currency types, formatting, and careful distribution and rounding without loss.
The CLR doesn't include a native money type. This could be seen as a shortcoming, or a reasonable design decision, given that it is an object-oriented framework, and adding your own type to encapsulate the data and behavior you need is pretty much its reason of being.
Money, however, is a type which is so primitive, and so pervasive, that its absence is notable. Other primitive types are built-in, after all. Martin Fowler considers this in Patterns of Enterprise Application Architecture [PEAA]:
A large proportion of the computers in this world manipulate money, so it's always puzzled me that money isn't actually a first class data type in any mainstream programming language. The lack of a type causes problems, the most obvious surrounding currencies. ... The good thing about object-oriented programming is that you can fix these problems by creating a Money class that handles them. Of course, it's still surprising that none of the mainstream base class libraries actually do this. (p. 488)
Using the Code
Money type is easy: it works just like any other numeric type.
Money m1 = 1.25;
Money m2 = 0.75;
Money total = m1 + m2;
Money difference = m1 - m2;
You can create
Money instances with a different currency than your current culture's currency:
Money m3 = new Money(1.25, Currency.Eur);
...but you can't perform operations on two instances with different currency types:
total = m2 + m3;
To distribute money without losing fractions, use a
Money amountToDistribute = 0.05M;
MoneyDistributor distributor = new MoneyDistributor(amountToDistribute,
Money distribution = distributor.Distribute(0.3M);
Assert.Equal(new Money(0.01M), distribution);
Assert.Equal(new Money(0.02M), distribution);
Assert.Equal(new Money(0.02M), distribution);
That's all there is to using it! However, if you are interested in a good case-study of the inputs, rationale, and struggles of a type design, read on...
Analysis of Approaches to a Money Type for the CLR
Approach in this Implementation
In [PEAA], Martin Fowler and Matt Foemmel's example shows Money as an object with value semantics. In the CLR, a value type is a first-class entity, and so it makes sense to combine these two and make
Money a value type. This should also help deal with the issue of performance the authors bring up, since value types are not reference counted on the heap, meaning less pressure on the GC and therefore higher performance.
Also in [PEAA], the underlying type used to store the value was an
Int64 (long). The authors then scale the value by some power of 10 (0 - 3) to represent fractional units. In this type, I kept the integral value storage, but opted to represent the fraction as a completely separate
Int32, which is scaled by 10^9 (the largest power of 10 which fits into an
Int32). This allows storing much smaller fractions, which is useful for intermediate computations. Due to this, the type is a fixed-decimal point numeric representation. This is how relational databases often store money, so it makes a natural fit. Another alternative was to represent the money value with a
System.Decimal. The problem with this approach is that
System.Decimal is a binary floating-point type, and binary floating-point types give us all sorts of headaches when computing with them and not treating the round-off or computational error accumulation with extreme care. These issues can be avoided by working with whole numbers and scaling to represent fractions.
I opted to separate the responsibility to allocate money in a defined distribution into another class:
MoneyDistributor. The reasons for this are that I think it helps readability and conscientious use, and having both a divide operation and an allocate operation on the money class seemed to be a bit of a conflict of interest. By separating out the responsibility for not losing (or gaining!) fractions of money into a separate class, I force
Money to admit that it can't really do a good job at dividing itself up, and rather entrusts this to another class. Further, this then allows subclassing of
MoneyDistributor for custom behavior, which can no longer be done with
Money, since it is a value type.
With regard to the currency aspect of money, the CLR doesn't have this as an available type. Java follows ISO 4217, and so it is pretty safe to add something like this - just follow the spec. My first inclination was to just make a
Currency class with static fields: one for each currency. However, Jason Hunt's implementation used a
CultureInfo instance to represent the currency, and it got me to think about how this is already somewhat present in the BCL. Since the
CultureInfo classes are built around an
Int32 identifier known as the
LCID, it seems reasonable to use this as the currency identifier as well. However, after some observation, this turns out not to be a sound approach: not only are the culture-to-currency relationships not 1-to-1, but they are also not N-to-1, since some cultures use more than one currency. In the end, I used the ISO spec to generate some code to load lookup tables and keep everything related based on the ISO numeric code for any given currency. This code is an
Int32, and this serves as the only field in a
Currency instance, allowing me to represent the
Currency as a value type as well. This makes serialization of a
Money value quite simple: there are no reference fields in it! One last functionality of
Currency to point out:
IFormatProvider is implemented on it, so that when
ToString() is called on
Money, the associated
Currency instance is passed with it and it gets formatted as expected.
When I started working on this type, I didn't find an implementation of a money type for the CLR. Two things of note: the first is that I didn't quite search for the right keywords (apparently, no one calls the CLR by its name: "the CLR"; everyone refers to it in a roundabout way using "C#" or ".NET"), and the second is that a good implementation was published the same day completely independently, half way around the world! Let's compare these other implementations, with the goal of finding the best approach if one clearly exists.
|Value Type or Reference Type||Currency type||Supports ISO 4217||Fixed or floating||Handles distribution||Arithmetic operations||Handles formatting||Currency instance formats the value||Parses formatted strings|
|Jason Hunt's||Reference||CultureInfo / RegionInfo||No||Floating||No||Yes||Yes*||No||No|
|Michael R. Brumm's||Value||Enum/ table lookup||No||Floating||No||No||Yes*||No||Yes|
|chimeric69's||Value||CultureInfo / RegionInfo||No||Floating||No||Yes||Yes||No||Yes|
|Pascal Lindelauf's ||Reference||Custom reference type / Enum||Yes||Floating||Yes||Yes||Yes*||No||No|
|This implem-entation||Value||Custom value type / table lookup||Yes||Fixed||Yes||Yes||Yes||Yes||No|
|* (not as IFormattable)|
Given that value types are first-class in the CLR, it seems quite natural to use it as the basis for a money type. A given instance of
Money is considered equal to another instance when the values are equal. This is known as having value semantics. Fowler calls types which have this characteristic "value-objects". On the other hand, you give up certain flexibility when defining it to be a value type vs. a reference type, namely: inheritance. This can get to be a problem if you want to keep your money allocation/distribution code with the money type and then modify it in a subclass, or if your database mapping layer depends on a reference type to wrap with database mapping code. I've handled the first concern by delegating the distribution responsibility to a separate class, and the second concern might indicate that it's time to get a more robust mapping layer. Given that a value type is intrinsically serializable, it shouldn't be hard at all to store this type in a database. There are some variations on how to do this depending on whether you are using a floating point or fixed point format for the value. More on this in "Fixed or floating".
The major decision here is to determine if a custom type is used, or a combination, or
System.Globalization.RegionInfo to represent the currency, and, if a custom type is used, if it should be a full-fledged type or an enumeration. Although the
RegionInfo approach is immediately appealing, it leaves some ambiguities which are hard to resolve, since some regions use more than one currency, and various currencies are used in more than one region. The other drawback is that aspects of the ISO 4217 spec which covers currency are not directly accessible: notably the numeric code and the exponent. The custom type approach allows you to deal with this, but the enumeration approach leaves you with the need to lookup this information in a table or via some static accessor method. These could be encapsulated in the money type so users wouldn't need to handle the specifics outside of just the enumeration. The custom type approach seems to me to be the most sound, since there is a good amount of data to encapsulate (see "Supports ISO 4217"). The actual type could be a reference type, but since the data rarely changes, and is keyed by a numeric code, a value type with that code as the key into static lookup tables is efficient and allows the value to be embedded in the money type, making money values very portable. By building these static lookup tables from the ISO spec as well as enumerating
RegionInfo, you can have all the information you need about currency from a single
Int32 encapsulated in a value type.
The world standard which governs currencies will have an impact on any Currency type design. Or at least it should. If
RegionInfo is used, this spec is indirectly observed. However, as pointed out in "Currency type", there are ambiguities when using
RegionInfo to represent currency, and implementing the spec fully in its own type resolves these. There are several components to each currency covered in the spec: currency name, a symbol, an exponent to indicate the smallest generally used division of the currency, a three letter code, and a numeric code. It is this extra information which serves as a major factor of how to represent Currency as a custom type, as noted more completely in "Currency type".
Allocating funds over a number of divisions or distributions can lead to fractions of units being gained or lost, which can then be magnified by subsequent computation or storage and retrieval. People who handle money don't like this. In [PEAA], allocation is handled by picking the number of digits after the decimal to keep, and truncating the quotient of the division at that digit, then subtracting the sum of all quotients from the initial amount, and distributing the remainder among the quotients in smallest-decimal increments. The modification I make here from that text is the inclusion of the precision desired to truncate to (from 0 - 9 places after the decimal) as well as an enumeration of three methods of distribution: first-to-last, last-to-first, and random.
Here's a question the answer to which sure to raise some eyebrows to those who haven't a firm grasp on the consequences of using floating-point types. If you've dealt with binary floating point numbers with fractions that cannot be represented exactly in base 2, but can be represented exactly in base 10, you're going to lose money, and probably a lot more than just the fractions of currency units that it appears to be at first. That's why, for a money type, it has to be a fixed format which is stored in base 10. By way of example, 1/3 is not exactly representable in base 10 or base 2. In base 10, a common way to split a unit three ways is to take the fractional units and give them to the last distribution, e.g., $1 three ways would be: $0.33, $0.33, and $0.34. Both 0.33 and 0.34 are representable in base 10; however, they are not representable exactly as a finite-length base 2 number, and some precision will be lost when doing so. This loss is often compounded or magnified during operations. There is really only one right answer here: fixed base 10.
Another benefit of choosing a fixed base 10 number: most relational databases have an exact numeric type which stores the value in base 10, so no loss will be incurred when storing and retrieving these values from a database. This will not always be true with base 2 numbers, and database mapping code will need to account for this if they are used instead. If your database doesn't have a base 10 number to store the money in, or it is inadequate, developing a custom strategy using 12 bytes (a long and an int) or 16 bytes with currency (a long and two ints) is straightforward.
In the CLR, you are able to define operators on a custom type. Given that money is involved in plenty of arithmetical operations, it is natural to do so. The only ones which should be suspected are multiplication and division, since using these to get distributions of money which need to add up to a specific total (or starting amount) without any loss or gain is not generally possible. [PEAA] uses an "allocate" method on the money type to replace division in these cases. A decision can be made on whether to keep this method with the money type or delegate it to a specialized class. The first method means that you don't need to know about a separate type, and most good IDEs will show you the allocate method and a keen developer should notice it, but that there is a bit of tension in the
Money class on how and when to divide itself. And the second method means that you have a clearly defined responsibility, code which uses it standing out a bit more as something special going on, and the ability to subclass for custom behavior (assuming that
Money is a value type), but the need to discover and learn about a second class. I prefer the separate class: it could be argued to be purely style, but if a distribution behavior change is needed (although when pressed, I couldn't admit to knowing when this would be needed), this approach would make it much cleaner.
The CLR has a pattern in place to support formatting of a type to a string representation:
IFormattable. It makes good sense to support this interface and match developer expectations on how
ToString() should work.
Following on supporting
IFormattable, there is a standard formatting string to indicate that the value should be formatted as currency: "C". In order to support this universally, you can pass into
String.Format() a format provider which will be used to help format the type correctly. Using a currency instance as the
IFormattable instance also seems natural. The only problem with this is a confusion which might arise when using a different currency than what the money value is expressed in: if the currency isn't checked and an exception thrown on a difference, a money value will be expressed in a different currency but with the original currency's numeric value. Some kind of currency conversion might need to be done instead, but this appears too complex and unwieldy to me.
The inverse of formatting: parsing a string into an instance of money and related currency. No interface provides
Parse() and (in 2.0 and on)
TryParse(), but they are found as static methods on the BCL's primitive types. Seeing as we'd like to make our
Money type as similar to BCL primitive types as we can, in order to create the illusion that it is part of the BCL, implementing these two methods becomes part of our job.
- 2008-07-30: First version.
- 2008-08-01: Source updated