Re: Monetary calculations in CL
- From: Kaz Kylheku <kkylheku@xxxxxxxxx>
- Date: Thu, 14 Feb 2008 12:14:16 -0800 (PST)
On Feb 14, 8:08 am, "John Thingstad" <jpth...@xxxxxxxxx> wrote:
På Thu, 14 Feb 2008 13:50:18 +0100, skrev Greg Menke <guse...@xxxxxxxxxxx>:
Simple arithmetic on such calculations is inappropriate. Cobol handles
this sort of thing quite well (intentionally I am sure). CL's numeric
types will easily handle the required precision but the rounding rules
will probably be somewhat troublesome. The class-based approach is
probably the way to handle it- perhaps an "interest" class where the #
of places of significance and the rounding policy are supplied.
Gregm
Well the most straightforward way is Binary Coded Decimal (BCD).
One nibble (4 bits) is reserved for each digit 0-9. So you write a library
to access numbers this way.
(Many processors have support for this.) Writing such a library is a bit
cumbersome, but still easier than dealing with the imprecision explicitly
in bignums I would think. (Because humans think of precision in digits
not binary)
What? Given exact rationals, you can implement the rounding rules
arithmetically. You don't need to depend on how those numbers are
implemented.
E.g. suppose you are to round to the nearest 0.01 such that:
0.xy4... -> 0.xy
0.xy5 -> (if (evenp y) 0.xy (+ 0.xy 0.01))
0.xy5... -> 0.xy + 0.01
You multiply the input by 100 to obtain xy.zw. To get the y digit, if
necessary, truncate the number to integer, and compute the residue
modulo 10. If the fractional part is less than half, take the
truncated number and divide by 100. If it's greater than half, add 1
and divide by 100. If it's exactly half, apply the test to y.
Here is some code, which has a few simplifying assumptions. One is
that the input is rational. The second is that EXPT gives us a
rational (and even if the exponent is negative). This works in CLISP
but is nonportable.
(defun round-even-odd-rule (input digits)
(let* ((multiplier (expt 10 digits))
(sign (signum input))
(scaled (* (abs input) multiplier)))
(multiple-value-bind (int frac) (truncate scaled)
(* sign
(/ (cond
((< frac 1/2) int)
((> frac 1/2) (1+ int))
(t (let ((leastdigit (mod int 10)))
(if (evenp leastdigit)
int (1+ int)))))
multiplier)))))
;; round to integer:
(round-even-odd-rule 1/2 0) -> 1
;; round to multiple of 10:
(round-even-odd-rule 1/2 1) -> 0
;; round 1.125 to nearest hundredth:
;; should be 1.12 since 2 is even.
(round-even-odd-rule 1125/1000 2) -> 28/25 ;; i.e. 112/100 or 1.12
;; round 1.135 to nearest hundredth:
;; should be 1.14 since 3 is odd:
(round-even-odd-rule 1135/1000 2) -> 57/50 ;; i.e. 114/100
;; round 7885 to nearest thousand:
(round-even-odd-rule 7885 -3) -> 8000
Of course, with BCD, to do the rounding, you just duplicate the input
number, and then work on the digits directly. Converting BCD between
internal representation and text is also trivial. (Have I, as I
suspect, just summed up the complete gamut of the advantages of BCD?)
.
- Follow-Ups:
- Re: Monetary calculations in CL
- From: John Thingstad
- Re: Monetary calculations in CL
- References:
- Monetary calculations in CL
- From: Chaitanya Gupta
- Re: Monetary calculations in CL
- From: Kent M Pitman
- Re: Monetary calculations in CL
- From: Chaitanya Gupta
- Re: Monetary calculations in CL
- From: Greg Menke
- Re: Monetary calculations in CL
- From: John Thingstad
- Monetary calculations in CL
- Prev by Date: Re: dataflow implementation in Lisp?
- Next by Date: Re: dataflow implementation in Lisp?
- Previous by thread: Re: Monetary calculations in CL
- Next by thread: Re: Monetary calculations in CL
- Index(es):
Relevant Pages
|
Loading