From: Juanma Barranquero
Subject: A simple? rounding function
Date: 
Message-ID: <366bb7fb.66761598@talia.ibernet.es>
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I'm trying to write a rounding function, but with a few catches:

1) I want it to work for integers and single-floats (at least; that's
the two types I'm interested in).

2) I want to round in the "traditional" way, i.e. 4.5 rounds to 5.0
and not to the nearest even number.

3) I'm not rounding to the nearest integer, but to the nearest "unit".
Usual values for unit are 1, 5, 0.01 and 0.05. The rational is that
I'm treating money amounts in Spanish pesetas and euro, so for "exact"
computations I round to 1 (peseta) and 0.01 (euro, i.e., to
eurocents), but for other values (user prices, etc.) I have to round
to the nearest multiple of 5 (PTA or eurocents).

The standard round/fround functions don't work for me both because of
2) and 3)

The obvious definition (I'm leaving out declarations etc.)

(defun round-to-unit (number &optional (unit 1))
  (* (floor (+ number (/ unit 2))
	    unit)
     unit))

works OK with integers, but its behaviour, at least with ACL 5.0 for
Windows, is a bit misleading when rounding floats:

USER(8): (round-to-unit 0.5 0.01)
0.5
USER(9): (round-to-unit 0.3 0.01)
0.29999998
USER(10): 

Now I suppose that, strictly speaking, it's a matter of "real" number
representability etc., and not of rounding. But still is a PITA.

I've tried solutions like passing the unit as a double-float (it
works, but it returns a double-float) and/or rationalizing the
intermediate values, etc. For example,

(defun round-to-float (number &optional (unit 1.0))
  (float (* (floor (+ number (/ unit 2.0))
		    unit)
	    (rationalize unit))))

works as expected, but forces me to use two separate functions. So the
question I'm posing is not how to implement a rounding function, but
more like: Is there any good generalization? Some of you has been
there, done that and come out with an obvious solution I'm not seeing?

(Evidently, I could just convert all data to double-floats, do the
rounding and then converting it back to the appropriate type, or
manage all euro amounts as integer eurocent amounts and divide by 100
when printing results, but I don't like either solution. They don't
seem general nor elegant, though I'm probably wrong on both accounts
:)

                                                       /L/e/k/t/u


-----BEGIN PGP SIGNATURE-----
Version: PGPfreeware 5.5.3i; see <http://www.pgpi.com>

iQA/AwUBNme0Kf4C0a0jUw5YEQKvGwCggil7h/aDfKvm8d8MTkmtOUGPm/QAoJ9s
RcQKmNuVvSaUB+BgUA0lZwGq
=TRdx
-----END PGP SIGNATURE-----

From: Barry Margolin
Subject: Re: A simple? rounding function
Date: 
Message-ID: <woT92.16$9w2.3159@burlma1-snr1.gtei.net>
In article <·················@talia.ibernet.es>,
Juanma Barranquero <···········@laley-actualidad.es> wrote:
>The obvious definition (I'm leaving out declarations etc.)
>
>(defun round-to-unit (number &optional (unit 1))
>  (* (floor (+ number (/ unit 2))
>	    unit)
>     unit))
>
>works OK with integers, but its behaviour, at least with ACL 5.0 for
>Windows, is a bit misleading when rounding floats:
>
>USER(8): (round-to-unit 0.5 0.01)
>0.5
>USER(9): (round-to-unit 0.3 0.01)
>0.29999998
>USER(10): 
>
>Now I suppose that, strictly speaking, it's a matter of "real" number
>representability etc., and not of rounding. But still is a PITA.

Actually, it's probably round-off error in the computations.  That's a fact
of life when you use floating point.

People who program applications designed to work with money generally use
fixed point math rather than floating point.  Just scale everything to be a
multiple of the smallest unit you have to deal with.  When printing out,
use FLOOR and REMAINDER to get the parts before and after the decimal point.

Languages like COBOL and PL/I have this stuff built-in, but it generally
hasn't been incorporated into other languages, which didn't have financial
computing as one of their primary goals.

-- 
Barry Margolin, ······@bbnplanet.com
GTE Internetworking, Powered by BBN, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Don't bother cc'ing followups to me.
From: Juanma Barranquero
Subject: Re: A simple? rounding function
Date: 
Message-ID: <36690e64.6242716@talia.ibernet.es>
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On Fri, 04 Dec 1998 15:41:16 GMT, Barry Margolin
<······@bbnplanet.com> wrote:

>Actually, it's probably round-off error in the computations.
>That's a fact of life when you use floating point.

Well, that's what I was also saying, thought with a less precise
wording than yours ;)

>People who program applications designed to work with money
>generally use fixed point math rather than floating point.

Yes, as I pointed out, that's one easy solution to my current
situation, and anyway, making the single-float version work is easy
enough if I'm adamant in using floats. But the question still is:
there's an easy, general ("general" as in "working for all CL real
hierarchy") way of doing that?

Thanks for your answer,

                                                       /L/e/k/t/u

-----BEGIN PGP SIGNATURE-----
Version: PGPfreeware 5.5.3i; see <http://www.pgpi.com>

iQA/AwUBNmgBgf4C0a0jUw5YEQLfBQCeIzxbtkjhzUMDFMq2ro1FNO3UjSMAoIKC
iTOwTk9BI/i62HOUNjyu3Rc7
=mGAo
-----END PGP SIGNATURE-----
From: Sunil Mishra
Subject: Re: A simple? rounding function
Date: 
Message-ID: <efy4srbhes5.fsf@hustle.cc.gatech.edu>
···········@laley-actualidad.es (Juanma Barranquero) writes:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> I'm trying to write a rounding function, but with a few catches:
> 
> 1) I want it to work for integers and single-floats (at least; that's
> the two types I'm interested in).
> 
> 2) I want to round in the "traditional" way, i.e. 4.5 rounds to 5.0
> and not to the nearest even number.
> 
> 3) I'm not rounding to the nearest integer, but to the nearest "unit".
> Usual values for unit are 1, 5, 0.01 and 0.05. The rational is that
> I'm treating money amounts in Spanish pesetas and euro, so for "exact"
> computations I round to 1 (peseta) and 0.01 (euro, i.e., to
> eurocents), but for other values (user prices, etc.) I have to round
> to the nearest multiple of 5 (PTA or eurocents).

All of these can be done with the function round.

Examples:

CL-USER 6 > pi
3.141592653589793D0

CL-USER 13 > (round pi)
3
0.14159265358979312D0

CL-USER 14 > (round pi 0.5)
6
0.14159265358979312D0

CL-USER 15 > (round pi 0.1)
31
0.041592607396193415D0


Sunil
From: Gareth McCaughan
Subject: Re: A simple? rounding function
Date: 
Message-ID: <86yaokjgx1.fsf@g.pet.cam.ac.uk>
Sunil Mishra wrote:

[someone else said, inter alia:]
>> 2) I want to round in the "traditional" way, i.e. 4.5 rounds to 5.0
>> and not to the nearest even number.
...
> All of these can be done with the function round.

Not true. If your ROUND rounds 4.5 to 5.0 then it's not ANSI.

-- 
Gareth McCaughan       Dept. of Pure Mathematics & Mathematical Statistics,
·····@dpmms.cam.ac.uk  Cambridge University, England.
From: David Steuber "The Interloper
Subject: Re: A simple? rounding function
Date: 
Message-ID: <3668a31e.354153375@news.newsguy.com>
On Fri, 04 Dec 1998 11:06:43 GMT, ···········@laley-actualidad.es
(Juanma Barranquero) claimed or asked:

% I'm trying to write a rounding function, but with a few catches:

% 3) I'm not rounding to the nearest integer, but to the nearest "unit".
% Usual values for unit are 1, 5, 0.01 and 0.05. The rational is that
% I'm treating money amounts in Spanish pesetas and euro, so for "exact"
% computations I round to 1 (peseta) and 0.01 (euro, i.e., to
% eurocents), but for other values (user prices, etc.) I have to round
% to the nearest multiple of 5 (PTA or eurocents).

My Lisp is no where near up to speed yet.  But if I were doing this in
C/C++, I would use BCD arithmetic to avoid the problems of .1 not
being represented in a finite number of digits, etc.  Is there a BCD
number type in Lisp?  Can one be conjured up that can use the BCD
functionality of the FPU, if it is available like on the i386 class of
CPU?

By rounding up all the time, you are going to be creating money.  The
reason for rounding to the nearest even is to eliminate systemic error
in rounding.  My apologies if you know this already.

--
David Steuber (ver 1.31.3a)
http://www.david-steuber.com
To reply by e-mail, replace trashcan with david.

May the source be with you...
From: Johan Kullstam
Subject: Re: A simple? rounding function
Date: 
Message-ID: <u3e6pkxz5.fsf@res.raytheon.com>
········@david-steuber.com (David Steuber "The Interloper") writes:

> On Fri, 04 Dec 1998 11:06:43 GMT, ···········@laley-actualidad.es
> (Juanma Barranquero) claimed or asked:
> 
> % I'm trying to write a rounding function, but with a few catches:
> 
> % 3) I'm not rounding to the nearest integer, but to the nearest "unit".
> % Usual values for unit are 1, 5, 0.01 and 0.05. The rational is that
> % I'm treating money amounts in Spanish pesetas and euro, so for "exact"
> % computations I round to 1 (peseta) and 0.01 (euro, i.e., to
> % eurocents), but for other values (user prices, etc.) I have to round
> % to the nearest multiple of 5 (PTA or eurocents).
> 
> My Lisp is no where near up to speed yet.  But if I were doing this in
> C/C++, I would use BCD arithmetic to avoid the problems of .1 not
> being represented in a finite number of digits, etc.  Is there a BCD
> number type in Lisp?  Can one be conjured up that can use the BCD
> functionality of the FPU, if it is available like on the i386 class of
> CPU?

i don't think C/C++ has a BCD type.  cobol is the only language of
which i know has a BCD type.  however you can use integer number of
cents (or smallest monetary unit) and let the dollar amount be that
divided by 100.  this would work in either lisp or C/C++.

floating point numbers are always a bit tricky.  this is why
finanicial packages have historically avoided them.

> By rounding up all the time, you are going to be creating money.  The
> reason for rounding to the nearest even is to eliminate systemic error
> in rounding.  My apologies if you know this already.

perhaps he is trying to set store prices and would like an upward bias
in the rounding mechanism.  finance doesn't always follow logic.

-- 
johan kullstam
From: David Steuber "The Interloper
Subject: Re: A simple? rounding function
Date: 
Message-ID: <366f4f73.791486938@news.newsguy.com>
On 09 Dec 1998 12:48:46 -0500, Johan Kullstam <·······@idt.net>
claimed or asked:

% i don't think C/C++ has a BCD type.  cobol is the only language of
% which i know has a BCD type.  however you can use integer number of
% cents (or smallest monetary unit) and let the dollar amount be that
% divided by 100.  this would work in either lisp or C/C++.

This is an implementation dependant issue, not a language standard.
Borland C++ (the last version I worked with) supported BCD arithmetic
with a C++ BCD class.  The operations were inlined assembler.  The x87
family of FPUs have BCD hardware.

I have done fixed radix arithmetic in assembler, but I used binary
radix points rather than decimal.  In that case, I wanted to multiply
two 32 bit integers and keep the 64 bit result in the eax:edx
registers.  I couldn't do this in in-line assembly because the Borland
compiler only went up to 286 code.  You needed TASM for the 386 and
486 assembly.  That was quite a while ago.

In Lisp, I would attempt to use fixnums or something that wouldn't
give me the decimal round off error that is inevitable when dealing
with floating point arithmetic, even double or extended (80 bit)
double precision.

--
David Steuber (ver 1.31.3a)
http://www.david-steuber.com
To reply by e-mail, replace trashcan with david.

May the source be with you...