CSC/ECE 517 Fall 2012/ch6 1w49 aa: Difference between revisions
No edit summary |
|||
Line 13: | Line 13: | ||
Money can be represented using floating point numbers. At a first look, this seems the most logical approach to handle money in programs. While floating-point data types are capable of representing extremely large positive and negative numbers and offer the equivalent of many decimal digits of precision, they are nonetheless inexact when it comes to representing decimal numbers. For example, 9.47 is stored as 9.479999542236328125. Thus performing arithmetic operations on such numbers further adds inaccuracy to the result. <ref name = floatRef /> | Money can be represented using floating point numbers. At a first look, this seems the most logical approach to handle money in programs. While floating-point data types are capable of representing extremely large positive and negative numbers and offer the equivalent of many decimal digits of precision, they are nonetheless inexact when it comes to representing decimal numbers. For example, 9.47 is stored as 9.479999542236328125. Thus performing arithmetic operations on such numbers further adds inaccuracy to the result. <ref name = floatRef /> | ||
=== Integer Representation | === Integer Representation <ref name = intRef /> === | ||
A single Integer can be used to represent money value. Such an approach requires an assumption of an implicit decimal point. For example $24.68 can be represented as 2468 considering the fact that for US Dollar representation, the decimal point is before the last two digits. This approach has the advantage of requiring less storage and accuracy in calculations. Drawbacks however crawl in when dealing with money belonging to different currencies having different implicit decimal points. | A single Integer can be used to represent money value. Such an approach requires an assumption of an implicit decimal point. For example $24.68 can be represented as 2468 considering the fact that for US Dollar representation, the decimal point is before the last two digits. This approach has the advantage of requiring less storage and accuracy in calculations. Drawbacks however crawl in when dealing with money belonging to different currencies having different implicit decimal points. |
Revision as of 00:26, 9 October 2012
Representing money - Skrien Chapter 6 gives an example of a class that can be used to represent money. But how is it done in real programs? Investigate, and report on the advantages and disadvantages of other approaches vs. Skrien's.
Introduction
In most commercial applications we are required to create, manipulate and store currency values. The most important concerns about the representation and storage of money are accuracy and consistency. While manipulating currency values the precision to which calculations are done will affect the accuracy. Also while storing and retrieving the currency values, the correct storage types will ensure consistency. These concerns become even more significant in scenarios where we have multiple currencies and conversion operations between them. This page first discusses the merits of an approach described by Skrien and also critically analyses a number of other approaches and their relative advantages and disadvantages. This page first discusses the design approach described by Skrien. Most of the real program implementations are also inspired from the same approach. We discuss some of them, their advantages and disadvantages in the following sections.
Representation using Primitive Data Types
Floating point Representation
Money can be represented using floating point numbers. At a first look, this seems the most logical approach to handle money in programs. While floating-point data types are capable of representing extremely large positive and negative numbers and offer the equivalent of many decimal digits of precision, they are nonetheless inexact when it comes to representing decimal numbers. For example, 9.47 is stored as 9.479999542236328125. Thus performing arithmetic operations on such numbers further adds inaccuracy to the result. <ref name = floatRef />
Integer Representation <ref name = intRef />
A single Integer can be used to represent money value. Such an approach requires an assumption of an implicit decimal point. For example $24.68 can be represented as 2468 considering the fact that for US Dollar representation, the decimal point is before the last two digits. This approach has the advantage of requiring less storage and accuracy in calculations. Drawbacks however crawl in when dealing with money belonging to different currencies having different implicit decimal points.
Two Integers
Two different integers can be used to represent the value of currencies with a fractional component. The first part represents the main and next represents the decimal. The apparent advantage of this approach is that displaying of the value will be simpler. Also the loss of accuracy in case of floating point data types can be avoided. The code in this approach must be able to handle the overhead of calculations for carry over. The main component value has to be increased by 1 when the secondary value goes beyond 100. The overhead becomes very significant when applied to large scale calculations. Storage overheads must also be considered. Another significant drawback is that such a system cannot represent fractions of a fundamental unit of currency. For example, in the United States, gasoline prices are typically represented with a precision of tenths of a cent per gallon. Another example is the stock market, where until early in the 21st century, stocks were traded in units of sixteenths of a dollar (6.25 cents.)<ref name = stockreference />
Class Representation
In Object Oriented programming languages, a class representation can be used for money. Such a class provides encapsulation and the users need not concern themselves as to how the positive and negative money is represented. An example of sample class representation is presented below.<ref name = skrienRef />
public class Money{ private long amount; private String currency; public Money (long amount, String currency) { this.amount = amount; this.currency = currency; } public String toString () { return amount/100 + “.” + amount%100 + currency; } public long getAmount() {..} public String getCurrency() {…} public int compareTo(Money m) {..} public Boolean equals(Object o) {..} public int hashCode() {..} public Money plus (Money) {..} public Money minus(Money){..} public Money times(double factor) {..} public Money dividedBy(double divisor) {..} public Money negate() {..} }
Such a class takes the currency attribute along with the amount to be represented. Using objects to represent money can also help in determining logical errors like adding denominations in different currencies. Such errors might slip through if we use primitive representation of money. The implementation of the toString method however becomes a terrible design with a large if-then-else statement for distinguishing between the layouts of 170 currencies. Such a class is fine as long as the money is in a single currency. It can however not be used if the money belongs to different currencies. For example we have a “wallet” with 10 Dollars, 50 Yens and 100 Rupees.
Skrien’s Approach
The representation discussed by Skrien in the book Object Oriented Programming Using Java, is built over the single class representation above. The approach has been modified to use Expert Design Pattern.
Design Considerations<ref name = skrienRef />
- Encapsulation of all major calculation functions. The programmer does not have to know details of the implementations.
- Using custom data type logical errors can be discovered. Many of these go without notice if primitive data-types are used because the compiler assumes the correct usage of the data without regard to the contextual use. Thus operations can be performed only by specialized functions and this prevents erroneous calculations.
- Most different types of currency have a similar implementation requirement. Thus the author suggests defining a currency instance variable to store currency specific values such as its symbol. The operations remain common across different currencies.
- A real world implementation of money must be able to support multiple types of currencies. The author suggest the implementation of a mixed money class that has supports all operations similar to money but can handle multiple currency types like a wallet. It stores the type and value of multiple types of currencies. The objects must be able to exchange values and support similar functions thus an interface is used which both money and mixed money must implement.
- The issue of conversion of values between currencies and comparison of values of different types, the author suggests a currency converter class. It must be recognized that the exchange rates are dynamic and thus cannot be coded in the mixed money class. The currency converter class only deals with the setting and usage of exchange rates. Objects of type money can use it to get multiplication factors and perform their own calculations.
This approach can be summarized by the following UML diagram.<ref name = currencyClass /><ref name = skrienRef />
The classes SimpleMoney and MixedMoney are used to represent money in cases when money belongs to only a single currency and when it belongs in parts to multiple currencies respectively. Simple money is a class as discussed in the previous section. Mixed money has the added features giving the list of currencies its object contains and the amounts in each currency and briefly looks as follows –
public class MixedMoney implements Money { public MixedMoney() {..} public string toString() {..} public Boolean equals(Object o) {..} public int hashCode() {..} public int compareTo(Money o) {..} public Money plus(Money) {..} public Money minus(Money) {..} public Money times(double factor) {..} public Money dividedBy(double divisor) {..} public Money negate() {..} public Collection getCurrencies() {..} public long getAmount (Currency currency) {..} }
For the ease of combining the objects of the two classes SimpleMoney and MixedMoney, the interface Money is used. This interface contains the common methods that need implementation in both the classes.
public interface Money extends Comparable<Money> { public int compareTo(Money o); public Money plus(Money money); public Money minus(Money money); public Money times(double factor); public Money dividedBy(double divisor); public Money negate(); }
Some Practically Used Approaches
Ruby - Money Gem
A gem named Money provides an implementation similar in some aspects as one specified by Skrien. It represents monetary values as integers in cents. Floating point rounding errors resulting from operations on decimals can be avoided using this implementation. The Money class uses Bank and Currency classes internally to define the attributes and behavior of a money object. It has built in ability to parse a money and currency string into corresponding Money/Currency object. The object of the type Money is immutable and conversions and operations result in creation of a new object except that the currency identifier can be changed after the creation of the money object. Mentioned below is an example of the usage of the money class
require 'money' # 10.00 USD money = Money.new(1000, "USD") money.cents #=> 1000 money.currency #=> Currency.new("USD") # Comparisons Money.new(1000, "USD") == Money.new(1000, "USD") #=> true Money.new(1000, "USD") == Money.new(100, "USD") #=> false Money.new(1000, "USD") == Money.new(1000, "EUR") #=> false Money.new(1000, "USD") != Money.new(1000, "EUR") #=> true # Arithmetic Money.new(1000, "USD") + Money.new(500, "USD") == Money.new(1500, "USD") Money.new(1000, "USD") - Money.new(200, "USD") == Money.new(800, "USD") Money.new(1000, "USD") / 5 == Money.new(200, "USD") Money.new(1000, "USD") * 5 == Money.new(5000, "USD") # Currency conversions some_code_to_setup_exchange_rates Money.new(1000, "USD").exchange_to("EUR") == Money.new(some_value, "EUR")
Each money object is associated with a bank object which is responsible for the currency exchange. The Bank class is also an internal class to the Money class but it resides at the class level. This means that the Bank object follows the Singleton pattern in that only one Bank object exists across all of the currency. The purpose of the Bank is to maintain information related to currency values such that currency conversion can take place.This object uses a class name VariableExchange which performs the exchange. By default the class has no knowledge of exchange rates as they change dynamically. The gem provides APIs for exchanging money from one currency to other. This can either be done by manually supplying exchange rates or by using exchange rates available at major banks and currency exchange websites.Since the Bank class inherits from an interface, this allows for extensible money conversion schemes such as being able to 'scrape' data from the internet and use it to populate conversion ratios.
bank = Money::Bank::VariableExchange.new bank.add_rate("USD", "CAD", 1.24515) bank.add_rate("CAD", "USD", 0.803115) c1 = 100_00.to_money("USD") c2 = 100_00.to_money("CAD") # Exchange 100 USD to CAD: bank.exchange_with(c1, "CAD") #=> #<Money @cents=1245150> # Exchange 100 CAD to USD: bank.exchange_with(c2, "USD") #=> #<Money @cents=803115>
An attribute of the type Currency stores the currency the money is in. The currency class by default has a table which is constant and has a list of known currencies. It specifies the three letter ISO4217<ref name = ISORef /> code for currency along with the monetary unit, their symbols and delimiters. The class also implements a CurrencyLoader class through which currency information can be loaded from specified locations. This allows the list to be easily manageable. Below is an example of the USD currency information loaded by a default JSON configuration file.
"usd": { "priority": 1, "iso_code": "USD", "name": "United States Dollar", "symbol": "$", "subunit": "Cent", "subunit_to_unit": 100, "symbol_first": true, "html_entity": "$", "decimal_mark": ".", "thousands_separator": ",", "iso_numeric": "840" },
The gem provides APIs for exchanging money from one currency to other. This can either be done by manually supplying exchange rates or by using exchange rates available at major banks and currency exchange websites. The implementation differs from Skrien's approach in one major aspect that is it does not have It is also worthwhile to mention a plugin named acts_as_money which makes it easier to work with money object. Inclusion of this gem allows the programmer to assume there are 2 columns in the database namely cents and currency. Mentioned below is an example of the implementation.
acts_as_money money :rate, :cents => :rate_in_cents, :currency => :rate_currency money :discount, :cents => :discount_in_cents, :currency => false end
acts_as_money allows you to pass a String, Fixnum, Float or Money object as a parameter to the setter, and it will call #to_money to convert it to a Money object. This makes it convenient for using money fields in forms.
r = Room.new :rate => "100.00" r.rate # returns <Money:0x249ef9c @currency="USD", @cents=10000>
Advantages
The internal CurrencyLoader class allows for easily extensible currency information sources, even allowing currency information to be dynamically loaded Due to the Bank implementation, additional class implementations can be loaded and changed out at runtime to allow for more complex, an potentially real-time, currency conversion information The fact that the Currency and Bank classes are internal to theMoney class, the user doesn't have to know about their implementation Ruby's dynamic nature allows for classes to be internal to Money, yet defined in separate files from the Money class, helping to keep the code modular from a developers point of view The addition of to_money() methods to basic Ruby primitives makes it easier to create money from just about any type Since Ruby supports arbitrarily large integer values, there is theoretically no upper bound to the amount of money that can be stored in a Money object
Disadvantages
Doesn't support multiple currency types in a single object A Money object is immutable except in one way, ability to change currency type, which is an inconsistency that could easily have been avoided While the addition of to_money() methods to primitive types makes some things easier, it adds the additional ability to incorrectly combine an object of type Money with one that is not Since the Bank object is a singleton contained within the Moneyclass, any modifications to currency exchange rates require locking of the Bank object which could result in performance degradation if exchange rates are modified often
Python - python-money
The python-money implementation<ref name = pythonRef /> of storing money in an object is a relatively simple library written in Python. As such, it does not have as much functionality nor the complexities that many other implementations have. The python-money module consists of two classes, Money and Currency. Much like other implementations, the Money object contains an amount and currency internally. The amount is specified as type Decimal<ref name = pythonDecRef/>, which allows storing the amount in a format similar to a float but without all of the precision issues that arise with using floats. When a Money object is created, either an identifier or Currency object is passed. Below is an example of the Currency data used for USD:
CURRENCY['USD'] = Currency(code='USD', numeric='840', name='US Dollar', countries=['AMERICAN SAMOA', 'BRITISH INDIAN OCEAN TERRITORY', 'ECUADOR', 'GUAM', 'MARSHALL ISLANDS', 'MICRONESIA', 'NORTHERN MARIANA ISLANDS', 'PALAU', 'PUERTO RICO', 'TIMOR-LESTE', 'TURKS AND CAICOS ISLANDS', 'UNITED STATES MINOR OUTLYING ISLANDS', 'VIRGIN ISLANDS (BRITISH)', 'VIRGIN ISLANDS (U.S.)'])
Within the namespace containing these classes, a default list of currency objects are created, indexed by their ISO4217 standard three character and number identifiers. This is what allows specifying currency by identifier rather than currency object. Unfortunately this limits the currency types to those statically defined in the source code. Additionally, since there is not a separate class to handle conversions, the exchange rate information is contained within the Currency object. This limits the way that Money objects can be converted.
Advantages
Much like an integer value in Ruby, a Decimal has a theoretically infinite precision and maximum value. As long as the user does not access the internal structure of the Money object directly, it behaves as an immutable object
Disadvantages
This implementation does not support an arbitrary number of decimal points Since the currency conversion data is set within the Currency object itself There is no locking present for the Currency object and there could be race conditions when users are simultaneously setting and getting exchange rates The current implementation can only convert to and from the default currency type Since this implementation can take multiple types of arguments as the monetary value but does not add methods to primitives for converting them to Money objects, type checking has to be done on arguments to determine how to handle them. This is something that we want to avoid as part of object oriented design.
Java
Apart from the implementations based on primitive data-types, BigDecimal data-type is used extensively for representation of money. A custom Money Class implementations can be used to provide a higher level of abstraction around the BigDecimal type. This makes it a more practical form of use. Here is an example snippet that shows the use of BigDecimal to perform some basic calculations on Money values.
import java.math.BigDecimal; import java.util.Currency; public class MoneyCalculation { private BigDecimal fAmountOne, fAmountTwo; private static int DECIMALS = 2; private static final BigDecimal TWO = new BigDecimal("2"); private static int ROUNDING_MODE = BigDecimal.ROUND_HALF_EVEN; private BigDecimal getSum(){ return fAmountOne.add(fAmountTwo); } private BigDecimal getAverage(){ return getSum().divide(TWO, ROUNDING_MODE); } }
One of the special cases of Java Money class implementation is found in Java Object-Relational Database Systems . Objects that are defined in Java are stored in FirstSQL/J as values in database columns. These are normal columns whose type is a Java class that is mapped to the appropriate database type internally.
Money Class Constructors: Money(BigDecimal amt, String currency); Money(double amt, String currency); Note: currency is a string name of a currency – ‘USD’, ‘Euro’, … Methods: String getCurrency(); // get currency type string String toString(); // get amount with standard formatting BigDecimal decValue(); // get numeric amount double doubleValue(); // get numeric amount
Advantages
The BigDecimal representation has built-in rounding modes. BigDecimal objects are immutable i.e. operations always return new objects, and do not modify the state of existing objects. The use of Currency class in custom Money implementations allows users to represent money in any currency. Conversions from one currency to another can be easily done with the use of a Currency object. An object oriented approach to Money allows encapsulation of methods such as adding, subtracting, negating money in one common implementation. The FirstSQL/J inbuilt querying capabilities provide significant elegant methods to get information about Money classes.
Disadvantages
Depending on the number of digits (<=9, <=18, >18), the corresponding types int, long, BigDecimal respectively are recommended for representation. This requires the user to be aware of what values the representation needs to take up. Primitive types double and float carry small rounding differences. Significant amount of logic is required for custom implementation of Mixed Money - different currencies being available. Certain inefficiencies of code can creep in if the user is not careful with some of the implementation details.
C++
Representations similar to the ones in Java are also seen in C++. However there are certain functions like "floor" that can be used in C++ for aiding in the right precision with decimal values for money quantities. The underlying data type for such cases is double. Example:
floor(double*21.32)
With this function, decimals are not lost because the double type, with its precision being greater than or equal to 15 digits, is used as a compiler supported long long. The only thing to be careful of is multiplying and dividing such values. The Zortech distribution has an in-built C++ Money Class in it.
class money { long dollars; int cents; // ... };
Advantages
The Zortech C++ money class is an efficient implementation. This is due to the fact that the operators use integer arithmetic. Not much overhead with maintaining the money class objects. Some nice routines such as "flatten" are made available using the money class. An example of this implementation from is shown here.
// Returns a money data item where the cents are // rounded modulo "cents". In this way cents can // be stripped of money items when the currency // does not have all the coins required to pay // every posible quantity. money flatten(const money& m, double cents, int rounding) { money temp; double c = floor(fabs(cents*money::SCALE())); // cents double r = fmod(m.m_money, c); // remainder temp.m_money = (!rounding || (2.0* r <= c) ? m.m_money - r : m.m_money - r + c ); return temp; }
Disadvantages
The long datatype in the Zortech implementation cannot hold more than 10 decimal digits. This money class implements most arithmetic operators, but it does not implement the multiply and divide operations. The user is left to implement these.
Representation of money in Database
Most custom implementations in the programming languages for money require a custom storage definition based on a combination of primitive data-types. Microsoft SQL Server provides an specific data-type to store currency values.<ref name = mssqlserverreference />. Although the representation is easy to store and retrieve but is not very widely used. It is important to analyse the relative advantages and disadvantages of this approach.
money
This data-type can be used to store monetary data values from -2^63 (-922,337,203,685,477.5808) through 2^63 - 1 (+922,337,203,685,477.5807), with accuracy to a ten-thousandth of a monetary unit.
smallmoney
This can be used for monetary data values from -214,748.3648 through +214,748.3647, with accuracy to a ten-thousandth of a monetary unit.
Advantages
The precision of the money data-type is like a DECIMAL(9,4) but technically is different in terms of actual implementation. It is similar to an integer implementation with an implied decimal point. This is enough for most implementations. The money data-type also supports formatting with a number of separators.
Disadvantages
The datatype is very rigid in terms of precision. The program has to use 4 decimal places. It is prone to precision losses when used in calculations.
References
<references> <ref name = floatRef> http://www.drdobbs.com/jvm/java-monetary-data/184405653 </ref> <ref name = intRef>http://users.csc.calpoly.edu/~jdalbey/SWE/SampleCode/Money.java </ref> <ref name = stockreference> http://www.infoplease.com/spot/stockdecimal1.html </ref> <ref name = skrienRef> Skrien, Dale. Object-Oriented Design Using Java. McGraw Hill, 2009, p. 184 </ref> <ref name = currencyClass> http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Currency.html </ref> <ref name = mssqlserverreference> http://msdn.microsoft.com/en-us/library/aa258271(v=sql.80).aspx </ref> <ref name = ISORef> http://www.abstracttechnology.com/standard/iso4217.html</ref> <ref name = pythonRef> http://code.google.com/p/python-money/ </ref> <ref name = pythonDecRef> http://docs.python.org/library/decimal.html</ref>