CSC/ECE 517 Fall 2012/ch6 1w49 aa: Difference between revisions
No edit summary |
|||
Line 7: | Line 7: | ||
== Representation using | == Representation using Primitive Data Types == | ||
'''Floating point Representation''' | '''Floating point Representation''' | ||
Line 25: | Line 25: | ||
'''Class Representation''' | '''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. 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. | 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. 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. | ||
== Ruby - Money Gem == | == Ruby - Money Gem == |
Revision as of 00:31, 4 October 2012
Representing money
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.
Integer Representation
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. 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.
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 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
References
<references> <ref name = stockreference> http://www.infoplease.com/spot/stockdecimal1.html </ref>