prePEP: Money data type

John Roth newsgroups at jhrothjr.com
Fri Oct 17 19:39:52 EDT 2003


I've made a few edits for spelling and grammar, but I
haven't cleaned up everything, and I haven't made
any substantive changes to the text. Do with it what
you will.

Substantive issues:

1. I've seen lots of strange rounding policies in
various nooks and crannies. Is there any way of
specifying a rounding policy that would be used
and inherited by the result money object?

2. Money objects are presumably immutable, the same
as all other numeric objects. I've edited item 4 to
presume this.

3. I haven't looked at Tim Peter's fixed point
module, so I don't know how he handled this.
Formatting characters should come from the
current locale, or else should be supplied by
an extended format method. In fact, I'd make
this a requirement: there should be a formatting
method that would handle the most common
commercial formatting requirements, including
zero and asterisk fill, floating and fixed currency
symbol, and blanking the entire output field
when it is zero. Also trailing minus sign, and
so forth.

4. Repr should round trip. That's basic
Python. This means that repr() should
return something that the money()
constructor understands regardless of
the current locale setting.
Making it human readable is the responsibility
of either str(), the % operator or a hypothetical
.printf() method.

6. On operators. What happens to precision
on multiplication and division?

John Roth

"Batista, Facundo" <FBatista at uniFON.com.ar> wrote in message
news:mailman.183.1066424190.2192.python-list at python.org...
Here I send it.

Suggestions and all kinds of recomendations are more than welcomed.

If it all goes ok, it'll be a PEP when I finish writing the code.

Thank you.

. Facundo


------------------------------------------------------------------------

PEP: XXXX
Title: Money data type
Version: $Revision: 0.1 $
Last-Modified: $Date: 2003/10/17 17:34:00 $
Author: Facundo Batista <fbatista at unifon.com.ar>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 17-Oct-2003
Python-Version: 2.3.3


Abstract
========

The idea is to make a Money data type, basically for financial uses, where
decimals are needed but floating point is too inexact.  The Money data type
should support the Python standard functions and operations.


Rationale
=========

The detail of the requeriments are in the `Requirements`_ section.  Here
I'll
include all the decisions made and why, and all the subjects still being
discussed.  The requirements will be numbered, to simplify discussion on
each point.

As an XP exercise, I'll write the test cases before the class itself, so
it'll
comply exactly the requeriments of those tests.  Please see them for an
exact specification (and if you propose a different behaviour, please
propose the corresponding test case if possible, thanks).


Why Not Use Tim Peters' FixedPoint?
--------------------------------------

As we'll see in Requeriments, there are items that FixedPoint does not
satisfy, either because it doesn't do something or does it differently.  It
could be extended or modified to comply with the Requeriments, but
some needs are specific to currency, and some features of FixedPoint are
unnecessarilly complex for Money, so taking them out will make this class
simplier.

Anyway, sometime maybe one could be made subclass of the other, or just
make one from both.  The code of the Money class is based in large part
on the code of Tim Peters' FixedPoint: thank you for your (very) valuable
ideas.


Open Issues
-------------------

6. About repr(). Should ``myMoney == eval(repr(myMoney))``?


Requirements
============

1. The syntax should be ``Money(value, [precision])``.

2. The value is one of the following:

       - another money (if you don't include *precision*, it will be
inheritated)

       - int or long (default *precision*: 0)::

           Money(45): 45
           Money(45, 2): 45.00
           Money(5000000000,3): 5000000000.000

       - float (*precision* is required)::

           Money(50.33, 3): 50.330

       - string (*precision* is extracted from the string)::

           Money('25.32'): 25.32
           Money('25.32', 4): 25.3200

       - any other object that could be input to the long() or float()
constructors.

3. Strings with floats in engineering notation will not be supported. This
is an unecessary complication.

4. Precision must be a non negative integer.

5. Attributes ``decimalSeparator``, ``currencySymbol`` and
   ``thousandSeparator`` could be overloaded, just to easy change them
   subclassing. This same *decimalSeparator* is that used by the constructor
   when receives a string. Defaults are::

       decimalSeparator = '.'
       currencySimbol = '$'
       thousandSeparator = ''

6. Calling repr() should not return str(self), because if the subclass
   indicates that ``decimalSeparator=''``, this could carry to a confusion.
   So, repr() should show a tuple of three values: IntPart, FracPart,
   Precision.

7. To comply the test case of Mark McEahern::

       cost = Money('5.99')
       percentDiscount = 10
       months = 3
       subTotal = cost * months
       discount = subTotal * (percentDiscount * 1.0) / 100
       total = subTotal - discount
       assertEqual(total, Money('16.17'))

8. To support the basic aritmetic (``+, -, *, /, //, **, %, divmod``) and
comparison (``==, !=, <, >, <=, >=, cmp``) operators in the following cases:

       - Money op Money
       - Money op otherType
       - otherType op Money
       - Money op= otherType

   OtherType could be int, float or long. Automaticlly will be converted to
   Money, inheritating the precision from the other component of the
   operation (and, in the case of the float, maybe losing precision
**before**  the operation).

   When both are Moneys, the result has the larger of the two precisions.

9. To support unary operators (``-, +, abs``).

10. To support the built-in methods:

        - min, max
        - float, int, long (int and long are rounded by Money)
        - str, repr
        - hash
        - copy, deepcopy
        - bool (0 is false, otherwise true)

11. To have methods that return its components. The value of Money will be
    ``(int part) + (frac part) / (10 ** precision)``.

        - ``getPrecision()``: the precision
        - ``getFracPart()``: the fractional part (as long)
        - ``getIntPart()``: the int part (as long)

12. The rounding to be financial. This means that to round a number in a
    position, if the digit at the right of that position is bigger than 5,
    the digit at the left of that position is incremented by one, if it's
    smaller than 5 isn't::

        1.123 --> 1.12
        1.128 --> 1.13

    But when the digit at the right of that position is ==5. There, if the
    digit at the left of that position is odd, it gets incremented,
otherwise
    isn't::

        1.125 --> 1.12
        1.135 --> 1.14


Reference Implementation
========================

To be included later:

- code
- test code
- documentation


Copyright
=========

This document has been placed in the public domain.






More information about the Python-list mailing list