source: release/1.5/source/contrib/krueger/InterfaceProjects/Utilities/decimal.lisp

Last change on this file was 13646, checked in by R. Matthew Emerson, 15 years ago

Merge r13631, r13636 from trunk. (Paul Krueger's updated InterfaceProjects
contrib; fix for ticket:652)

File size: 4.5 KB
Line 
1;; decimal.lisp
2
3#|
4The MIT license.
5
6Copyright (c) 2010 Paul L. Krueger
7
8Permission is hereby granted, free of charge, to any person obtaining a copy of this software
9and associated documentation files (the "Software"), to deal in the Software without restriction,
10including without limitation the rights to use, copy, modify, merge, publish, distribute,
11sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all copies or substantial
15portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
18LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23|#
24
25(eval-when (:compile-toplevel :load-toplevel :execute)
26 (require :ns-string-utils))
27
28(defpackage :interface-utilities
29 (:nicknames :iu)
30 (:export lisp-to-ns-decimal lisp-from-ns-decimal))
31
32(in-package :iu)
33
34;; This contains a set of utility functions for converting back and forth between
35;; NSDecimalNumber objects and lisp integers.
36;; Lisp does a good job representing large integers, so in general we can do things
37;; like manipulate dollar amounts just by using integers to represent the total
38;; cents value and doing something like:
39;; (floor lisp-value 100)
40;; to retrieve dollars and cents values.
41
42;; Using float values for dollars and cents is problematic. Rounding errors will get
43;; you anytime you convert to or from float-values and input or display strings. Apple
44;; has created a special class NSDecimalNumber to represent long decimal numbers. They
45;; defined an array of arithmetic functions to manipulate instance of this class. While
46;; we could probably just use those functions in lisp and manipulate NSDecimalNumber
47;; objects directly, it seems preferable to convert back and forth to lisp integers and
48;; use normal arithmetic operations when in Lisp. Of course it will be up to the Lisp
49;; programmer to understand how many decimal digits are being represented whereas this
50;; value is represented within NSDecimalNumberObjects.
51
52(defun lisp-to-ns-decimal (int-val &key (decimals 2))
53 ;; construct an NSDecimalNumber object with the given int-val and number of decimals
54 ;; For example if you have a dollar amount 123.45 represented by the fixnum 12345
55 ;; you would call (make-ns-decimal 12345 :decimals 2) to get a corresponding
56 ;; NSDecimalNumber object. This object is the responsibility of the caller and a
57 ;; call to #/release must be made when the caller is done with it.
58 (unless (typep int-val 'fixnum)
59 (error "Input must be a fixnum"))
60 (#/decimalNumberWithMantissa:exponent:isNegative:
61 ns:ns-decimal-number
62 (abs int-val)
63 (- decimals)
64 (if (minusp int-val) #$YES #$NO)))
65
66(defun lisp-from-ns-decimal (ns-dec-obj &key (decimals 2))
67 ;; This function returns a fixnum that corresponds to the NSDecimalNumber
68 ;; or NSNumber that is passed in as the first argument.
69 ;; The result will be scaled and rounded to represent the desired
70 ;; number of decimal digits as specified by the :decimals keyword argument.
71 ;; For example, if an NSNumber is passed in which is something like 123.45678
72 ;; and you ask for 2 decimal digits, the returned value will be the integer 12346.
73 (let* ((loc (#/currentLocale ns:ns-locale))
74 (lisp-str (ccl::lisp-string-from-nsstring
75 (#/descriptionWithLocale: ns-dec-obj loc)))
76 (str-len-1 (1- (length lisp-str)))
77 (dec-pos (or (position #\. lisp-str) str-len-1))
78 (dec-digits (- str-len-1 dec-pos))
79 (dec-diff (- decimals dec-digits))
80 (mantissa-str (delete #\. lisp-str)))
81 (cond ((zerop dec-diff)
82 (read-from-string mantissa-str))
83 ((plusp dec-diff)
84 (read-from-string (concatenate 'string
85 mantissa-str
86 (make-string dec-diff :initial-element #\0))))
87 (t ;; minusp dec-diff
88 (let ((first-dropped (+ (length mantissa-str) dec-diff)))
89 (+ (if (> (char-code (elt mantissa-str first-dropped)) (char-code #\4)) 1 0)
90 (read-from-string (subseq mantissa-str 0 first-dropped))))))))
91
92
Note: See TracBrowser for help on using the repository browser.