Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Formatting: separating commas for non-decimals

16 views
Skip to first unread message

Robert Monfera

unread,
Nov 13, 1999, 3:00:00 AM11/13/99
to
Hello,

I needed to format a number such that there are separating commas at
every third digit (dollars), and two decimals for the cents:

3,352,194.82

I haven't found any directive for this one, so I glued it together with
the help of FLOOR. I looked at the CLHS and CLtL2 - I feel I must have
overlooked something.

Thanks,
Robert

Robert Monfera

unread,
Nov 13, 1999, 3:00:00 AM11/13/99
to
Hello,

3,352,194.82

overlooked something?

Thanks,
Robert

Don Geddis

unread,
Nov 13, 1999, 3:00:00 AM11/13/99
to
On Sat, 13 Nov 1999 14:07:43 -0500, Robert Monfera <mon...@fisec.com> wrote:
> I needed to format a number such that there are separating commas at
> every third digit (dollars), and two decimals for the cents:
> 3,352,194.82

Sounds like a pretty typical problem. I had the same one, and was similarly
surprised that Lisp didn't seem to have any built-in way of doing this.

> I haven't found any directive for this one, so I glued it together with
> the help of FLOOR. I looked at the CLHS and CLtL2 - I feel I must have

> overlooked something.

Best I came up with was:
(multiple-value-bind (dollars cents) (truncate price)
(format t "$~:D~2,0$" dollars cents) )

_____________________________________________________________________________
Don Geddis ged...@cadabra.com Phone 650-403-2220
Cadabra Inc. http://cadabra.com Fax 650-403-2201
1820 Gateway Drive, Suite 300, San Mateo, CA 94404 Main 650-403-2200
On two occasions I have been asked, "Pray, Mr. Babbage, if you put into the
machine wrong figures, will the right answers come out?"

David B. Lamkins

unread,
Nov 14, 1999, 3:00:00 AM11/14/99
to
In article <382DB704...@fisec.com>, mon...@fisec.com wrote:

>Hello,


>
>I needed to format a number such that there are separating commas at
>every third digit (dollars), and two decimals for the cents:
>
>3,352,194.82
>

>I haven't found any directive for this one, so I glued it together with
>the help of FLOOR. I looked at the CLHS and CLtL2 - I feel I must have

>overlooked something?

I couldn't find anything, either.

I ended up with this:

? (multiple-value-bind (int frac)
(floor 12345678.90)
(format nil "~:D~2,0$" int frac))
"12,345,678.90"

Russell Senior

unread,
Nov 14, 1999, 3:00:00 AM11/14/99
to
>>>>> "David" == David B Lamkins <dlam...@psg.com> writes:

>> I haven't found any directive for this one, so I glued it together
>> with the help of FLOOR. I looked at the CLHS and CLtL2 - I feel I
>> must have overlooked something?

David> I couldn't find anything, either.

David> I ended up with this:

David> ? (multiple-value-bind (int frac) (floor 12345678.90) (format
David> nil "~:D~2,0$" int frac)) "12,345,678.90"

That doesn't work on negative numbers. How about:

(defun print-money (x)
(multiple-value-bind (dollars cents) (truncate x)
(format nil "~:D~2,0$" dollars (abs cents))))


--
Russell Senior ``The two chiefs turned to each other.
sen...@teleport.com Bellison uncorked a flood of horrible
profanity, which, translated meant, `This is
extremely unusual.' ''

Erik Naggum

unread,
Nov 15, 1999, 3:00:00 AM11/15/99
to
* Robert Monfera <mon...@fisec.com>

| I needed to format a number such that there are separating commas at
| every third digit (dollars), and two decimals for the cents:

(in-package :cl-user)

(declaim (declaration :author :date :copyright))
(declaim (:author "Erik Naggum")
(:date "1997-08-12")
(:copyright "Copyright 1997 by Erik Naggum.
Any use is permitted provided that this copyright notice is retained and
that all changes are duly identified."))

(defun dollars (stream amount colon-p atsign-p
&optional (width 0) (padchar #\Space) (commachar #\,))
"Print an amount of dollars for humans to read from FORMAT.

The full form is ~WIDTH,PADCHAR,COMMACHAR:@/DOLLARS/, where WIDTH is the
minimum width of the field (default 0), PADCHAR is the character used to
pad to the width on the left end, and COMMACHAR is the separator between
each group of three digits.

The @ modifier controls printing of the sign of positive amounts. If
omitted, a positive value prints without a sign. Otherwise, a positive
amount has an explicit + sign.

The : modifier controls the position of the sign and the padding. If
omitted, the dollar sign is printed in the leftmost position, then any
intervening pad characters, then the signed value. Otherwise, the sign
occupies the leftmost position, and the dollar sign follows any intervening
pad characters."
(let* ((digits
(multiple-value-bind (dollars cents) (floor (abs amount) 1)
(format nil "~,,V:D.~2,'0D" commachar dollars (round cents 0.01))))
(sign (if (minusp amount) #\- (if atsign-p #\+ nil)))
(padding (max 0 (- width 1 (if sign 1 0) (length digits))))
(pre (if colon-p sign #\$))
(post (if colon-p #\$ sign)))
(when pre (write-char pre stream))
(dotimes (i padding) (write-char padchar stream))
(when post (write-char post stream))
(write-string digits stream)))


#:Erik
--
Attention Microsoft Shoppers! MS Monopoly Money 6.0 are now worthless.

Marco Antoniotti

unread,
Nov 15, 1999, 3:00:00 AM11/15/99
to

Russell Senior <sen...@teleport.com> writes:

> >>>>> "David" == David B Lamkins <dlam...@psg.com> writes:
>
> >> I haven't found any directive for this one, so I glued it together
> >> with the help of FLOOR. I looked at the CLHS and CLtL2 - I feel I
> >> must have overlooked something?
>
> David> I couldn't find anything, either.
>
> David> I ended up with this:
>
> David> ? (multiple-value-bind (int frac) (floor 12345678.90) (format
> David> nil "~:D~2,0$" int frac)) "12,345,678.90"
>
> That doesn't work on negative numbers. How about:
>
> (defun print-money (x)
> (multiple-value-bind (dollars cents) (truncate x)
> (format nil "~:D~2,0$" dollars (abs cents))))

I know CL is an ANSI standard, but in Italy you would write

21.975.308.442

(at the exchange rate of 1.780 ITL per USD). I.e. you'd use points
instead of commas. :)

So, this is an issue of "internationalization". Of course, imposing
the MKS system to the Anglo-Saxon world is a matter of
"rationalization" :) Unless you want to end up like in the "Babylon 5"
universe. :)

Cheers

--
Marco Antoniotti ===========================================
PARADES, Via San Pantaleo 66, I-00186 Rome, ITALY
tel. +39 - 06 68 10 03 17, fax. +39 - 06 68 80 79 26
http://www.parades.rm.cnr.it/~marcoxa

Erik Naggum

unread,
Nov 15, 1999, 3:00:00 AM11/15/99
to
* Marco Antoniotti <mar...@parades.rm.cnr.it>

| I know CL is an ANSI standard, but in Italy you would write
|
| 21.975.308.442
|
| (at the exchange rate of 1.780 ITL per USD). I.e. you'd use points
| instead of commas. :)

(defun print-money (lire)
(format nil "ITL ~,,'.:D" lire))

(print-money 21975308442)
=> "ITL 21.975.308.442"

| So, this is an issue of "internationalization".

using the period (espesially when mislabeled "full stop") as the grouping
delimiter and comma as the fraction mark is REALLY STUPID. as early as
in second grade, when I was first exposed to the other use of comma, I
raised my hand and asked the completely befuddled teacher why we used the
comma both for decimal point and a list separator when that clearly made
it easy to get things wrong. I recall that she got angry when I insisted
it was dumb, and I can probably trace my years of arduous involvement in
standards committees to these traumatic early childhood events. the
really sad thing is that ISO buys into this comma crap, too.

Sam Steingold

unread,
Nov 15, 1999, 3:00:00 AM11/15/99
to
>>>> In message <31516405...@naggum.no>
>>>> On the subject of "Re: Formatting: separating commas for non-decimals"
>>>> Sent on 15 Nov 1999 07:42:27 +0000

This is broken:

> (format t "~/dollars/" 12345678.999)
$12,345,678.100

The correct version is (GPL2):

(defun comma (stream num colon-p atsign-p
&optional (dd 0 ddp) (di 0) (padchar #\Space) (commachar #\,))
"Print the number, commified.
Can be used in `format' as ~cents,dollars,padchar,commachar,dollchar/comma/,
where `cents' and `dollars' are the widths of the corresponding fields,
padchar is the character used to pad to the width on the left end, and
commachar is the separator between each group of three digits, dollarchar
is the currency character.


The @ modifier controls printing of the sign of positive amounts. If
omitted, a positive value prints without a sign. Otherwise, a positive
amount has an explicit + sign.

The : modifier controls the presence of the dollar sign: if given,
the dollar sign is printed right before the first digit, if not, no
dollar sign is printed."
(declare (stream stream) (fixnum dd di) (number num)
(character padchar commachar))
(multiple-value-bind (inum dnum) (truncate num)
(declare (integer inum))
(let ((dnum (abs (dfloat dnum))))
(declare (double-float dnum))
(unless ddp (setq dd (1- (length (format nil "~f" dnum)))))
(when (< (- 1 dnum) (expt 1/10 dd)) ; rounding at formatting
(incf inum (signum inum))
(setq dnum 0.0d0))
(let ((str (format nil "~,,v:d~v,vf" commachar inum (1+ dd) dd dnum))
(sig (if (and atsign-p (plusp num)) #\+)))
(declare (simple-string str))
(format stream "~v,,,va~@[~c~]~@[~c~]~a"
(max 0 (- (+ 1 dd di) (length str)
(if sig 1 0) (if colon-p 1 0)))
padchar "" (if colon-p #\$) sig str)))))


--
Sam Steingold (http://www.podval.org/~sds/)
Micros**t is not the answer. Micros**t is a question, and the answer is Linux,
(http://www.linux.org) the choice of the GNU (http://www.gnu.org) generation.
will write code that writes code that writes code for food

Robert Monfera

unread,
Nov 15, 1999, 3:00:00 AM11/15/99
to

Marco Antoniotti wrote:
...


> I know CL is an ANSI standard, but in Italy you would write
>
> 21.975.308.442

Generally, continental Europe uses decimal commas and separating points,
I think:

21.975.308.442,92 (OK, this was Euro rather than Lira :-)

> So, this is an issue of "internationalization". Of course, imposing
> the MKS system to the Anglo-Saxon world is a matter of
> "rationalization" :)

I'm wondering what ISO has to say about it. Erik seems to imply they
are going with the Old Continent method. It may be irrelevant, as
Americans still measure liquids in pints, tablespoons and cubic inches,
and their paper size is Letter rather than A4.

This comma versus point issue is not too confusing, as there is usually
enough context to figure out the number. What is definitely worse is
the ambiguity of digits between the Continental and the Anglo-Saxon
world. If you write a European number 1 in the States, people will
swear that's a seven. To avoid it, I use European 7's and Anglo 1's:

------ |
/ |
--/-- |
/ |
/ |

Robert

David B. Lamkins

unread,
Nov 15, 1999, 3:00:00 AM11/15/99
to
In article <86k8nkm...@coulee.tdb.com>, Russell Senior
<sen...@teleport.com> wrote:

>>>>>> "David" == David B Lamkins <dlam...@psg.com> writes:
>
>>> I haven't found any directive for this one, so I glued it together
>>> with the help of FLOOR. I looked at the CLHS and CLtL2 - I feel I
>>> must have overlooked something?
>
>David> I couldn't find anything, either.
>
>David> I ended up with this:
>
>David> ? (multiple-value-bind (int frac) (floor 12345678.90) (format
>David> nil "~:D~2,0$" int frac)) "12,345,678.90"
>
>That doesn't work on negative numbers. How about:
>
>(defun print-money (x)
> (multiple-value-bind (dollars cents) (truncate x)
> (format nil "~:D~2,0$" dollars (abs cents))))

Yes, of course. Thanks for the correction.

Gareth McCaughan

unread,
Nov 16, 1999, 3:00:00 AM11/16/99
to
Sam Steingold wrote:

[SNIP: Erik's code]


> This is broken:
>
>> (format t "~/dollars/" 12345678.999)
> $12,345,678.100
>
> The correct version is (GPL2):

[SNIP: Sam's code]

This is also broken, because (1) it refers to the nonexistent
function DFLOAT, and (2) the documentation says it takes a dollar-character
argument but it doesn't. Oh, and also because (3) it's GPLed, which
makes it unusable by people writing non-RMSFree software.

It would be simpler to amend Erik's code a little: before


(format nil "~,,V:D.~2,'0D" commachar dollars (round cents 0.01))))

add
(when (>= 100 (setf cents (round cents 0.01)))
(incf dollars)
(setf cents 0))
(format nil "~,,V:D.~2,'0D" commachar dollars cents)))

On the other hand, Sam's code does a couple of things Erik's doesn't.

--
Gareth McCaughan Gareth.M...@pobox.com
sig under construction

Sam Steingold

unread,
Nov 16, 1999, 3:00:00 AM11/16/99
to
>>>> In message <86903zf...@g.local>

>>>> On the subject of "Re: Formatting: separating commas for non-decimals"
>>>> Sent on 16 Nov 1999 01:49:12 +0000

>>>> Honorable Gareth McCaughan <Gareth.M...@pobox.com> writes:
>>
>> This is also broken, because (1) it refers to the nonexistent
>> function DFLOAT, and (2) the documentation says it takes a

I am sorry.

(defmacro dfloat (num)
"Coerce to double float."
`(float ,num 1.0d0))

this is from http://www.podval.org/~sds/data/cllib.zip:base.lsp,
just as the code I posted was taken verbatim from
http://www.podval.org/~sds/data/cllib.zip:print.lsp

>> dollar-character argument but it doesn't. Oh, and also because (3)

huh? oh, oops! sorry! :-)
forgot the implement :-)
the following should work.

(defun comma (stream num colon-p atsign-p &optional (dd 0 ddp) (di 0)

(padchar #\Space) (commachar #\,) (dollarchar #\$))


"Print the number, commified.
Can be used in `format' as ~cents,dollars,padchar,commachar,dollchar/comma/,
where `cents' and `dollars' are the widths of the corresponding fields,
padchar is the character used to pad to the width on the left end, and
commachar is the separator between each group of three digits, dollarchar
is the currency character.
The @ modifier controls printing of the sign of positive amounts. If
omitted, a positive value prints without a sign. Otherwise, a positive
amount has an explicit + sign.
The : modifier controls the presence of the dollar sign: if given,
the dollar sign is printed right before the first digit, if not, no
dollar sign is printed."
(declare (stream stream) (fixnum dd di) (number num)

(character padchar commachar dollarchar))


(multiple-value-bind (inum dnum) (truncate num)
(declare (integer inum))

(let ((dnum (abs (float dnum 1.0d0))))


(declare (double-float dnum))
(unless ddp (setq dd (1- (length (format nil "~f" dnum)))))
(when (< (- 1 dnum) (expt 1/10 dd)) ; rounding at formatting
(incf inum (signum inum))
(setq dnum 0.0d0))
(let ((str (format nil "~,,v:d~v,vf" commachar inum (1+ dd) dd dnum))
(sig (if (and atsign-p (plusp num)) #\+)))
(declare (simple-string str))
(format stream "~v,,,va~@[~c~]~@[~c~]~a"
(max 0 (- (+ 1 dd di) (length str)
(if sig 1 0) (if colon-p 1 0)))

padchar "" (if colon-p dollarchar) sig str)))))


>> it's GPLed, which makes it unusable by people writing non-RMSFree
>> software.

like who? :-)
I say in http://www.podval.org/~sds/software.html, that I am willing to
consider changing the license to LGPL for those part which are needed by
those who cannot abide by the GPL.

>> On the other hand, Sam's code does a couple of things Erik's doesn't.

thanks.

--
Sam Steingold (http://www.podval.org/~sds/)
Micros**t is not the answer. Micros**t is a question, and the answer is Linux,
(http://www.linux.org) the choice of the GNU (http://www.gnu.org) generation.

MS DOS: Keyboard not found. Press F1 to continue.

0 new messages