Discussion:
rounding to specified number of decimal places
(too old to reply)
Nelson Alexandra
2020-03-20 16:19:11 UTC
Permalink
what would be the equivalent in common lisp of the function round() in
programming language R?

round() description in R is:

‘round’ rounds the values in its first argument to the specified
number of decimal places (default 0).

example: round(1.234,digits=2) ==> 1.23
Vladimir Sedach
2020-03-20 22:55:00 UTC
Permalink
--8<---------------cut here---------------start------------->8---
CL-USER> (* (round 1.234 0.01) 0.01)
1.23
--8<---------------cut here---------------end--------------->8---

Is this really what you want though? It is better to leave rounding
until it is time to print the number. The FORMAT ~F and ~$ directives
have a lot of useful options.
--
Vladimir Sedach
Software engineering services in Los Angeles https://oneofus.la
Nelson Alexandra
2020-03-21 13:09:03 UTC
Permalink
Post by Vladimir Sedach
--8<---------------cut here---------------start------------->8---
CL-USER> (* (round 1.234 0.01) 0.01)
1.23
--8<---------------cut here---------------end--------------->8---
Thank you very much. It helped me to better understand the
specification of ROUND in the hyperspec.
Post by Vladimir Sedach
Is this really what you want though? It is better to leave rounding
until it is time to print the number.
I thought about that: when does rounding actually make
sense beside formatting for output? And indeed the R-learning
material uses round() for formatting.

Also thanks for the hint to the FORMAT directives ~F, ~$.

Below I try to imagine how I could have come up with your
solution.

Given:
According to hyperspec
(http://www.lispworks.com/documentation/HyperSpec/Body/f_floorc.htm)
for ROUND we have quotient*divisor+remainder=number or short: q*d+r=n
and q always represents an integer. Let n = 1.235 (better example than
the original 1.234 because it needs a little bit more hyperspec
information).

Want: equivalent calculation to the R-Code round(1.235,digits=2).

First I have to translate the problem in to an "integer problem",
because the returned quotient of ROUND or FROUND represents always an
integer.
So according to hyperspec rounding rules I have
124 * d + r = 123.5/100. Then d = 1/100 and r follows.
Finally scaling q with 1/100.

As naive code:

(DEFUN r-round (x &OPTIONAL (digits 0))
(* (ROUND x (/ 1 (EXPT 10 digits))) (/ 1 (EXPT 10 digits))))
Madhu
2020-03-21 14:40:59 UTC
Permalink
Post by Nelson Alexandra
I thought about that: when does rounding actually make
sense beside formatting for output?
Good question.
Post by Nelson Alexandra
And indeed the R-learning material uses round() for formatting.
First I have to translate the problem in to an "integer problem",
because the returned quotient of ROUND or FROUND represents always an
integer.
So according to hyperspec rounding rules I have
124 * d + r = 123.5/100. Then d = 1/100 and r follows.
Finally scaling q with 1/100.
(DEFUN r-round (x &OPTIONAL (digits 0))
(* (ROUND x (/ 1 (EXPT 10 digits))) (/ 1 (EXPT 10 digits))))
Converting this to an integer problem means the result of your function
is a rational, not a float. You may have to (coerce ret 'float) or
use do something like (if (integerp x) x (coerce ret (type-of x)))
to convert ret (the return value) to the appropriate float type which
you passed in through x.

I'd have assumed that R's `round' did something which we learnt in 3rd
or 4th grade: 1.235 would be rounded of to 1.24 and 1.233 would be
rounded off to 1.23. Remind me if you know because I've forgotten:
where is that convention of rounding to significant digits (after the
decimal point) even used ?
Kaz Kylheku
2020-03-22 04:50:57 UTC
Permalink
Post by Vladimir Sedach
--8<---------------cut here---------------start------------->8---
CL-USER> (* (round 1.234 0.01) 0.01)
1.23
--8<---------------cut here---------------end--------------->8---
Is this really what you want though? It is better to leave rounding
until it is time to print the number.
Ah, but not so! In scientific or engineering calculations, yes;
and even in financial models and such.

But suppose you're dealing with currency. Then you have to round every
calculation that is supposed to produce a monetary amount (e.g. dollars
and cents). For instance if some tax calculation produces $123.45678,
you need to make that into $123.46 before entering it into the ledger.
Jeff Barnett
2020-03-22 06:40:50 UTC
Permalink
Post by Kaz Kylheku
Post by Vladimir Sedach
--8<---------------cut here---------------start------------->8---
CL-USER> (* (round 1.234 0.01) 0.01)
1.23
--8<---------------cut here---------------end--------------->8---
Is this really what you want though? It is better to leave rounding
until it is time to print the number.
Ah, but not so! In scientific or engineering calculations, yes;
and even in financial models and such.
But suppose you're dealing with currency. Then you have to round every
calculation that is supposed to produce a monetary amount (e.g. dollars
and cents). For instance if some tax calculation produces $123.45678,
you need to make that into $123.46 before entering it into the ledger.
Sales tax calculation is one real life example but here's two more:

Example 1: Stock prices are reported in 1/8$ amounts. That's 12.5 cent
increments. When a bunch of shares are bought/sold, the bulk price may
have a fractional cent in it. N.B. Share blocks may even include
fractional numbers of shares. I'm not sure what happens to the fraction
but I'm guessing that neither you nor I will ever get it. So there is
some sort of rounding rule used in these transactions.

Example 2: When we were remodeling our kitchen I ran into this
silliness. Tile prices were quoted per square foot; I presume this made
it easier to compare relative costs of different tiles. Tile sizes were
just about anything, e.g., 11+1/3" x 12+1/8", and they were only ordered
and sold by the box (usually with 12 tiles per box). So I ask how much
did a box cost and nobody could answer the question without an abacus or
calculator. For a job, they actually figured out the area you wanted
covered plus extras for accidents, turned that into an integer number of
boxes, then calculated the number of sq ft in a box and multiplied by
the number of boxes they thought you were gong to buy, then multiplied
by the price per sq ft. This was from one of those "high" end national
franchises and this idiotic system was supplied by the franchising
organization along with the computer support.

So yes, fractional pennies appear in the real world and lots of
businesses deal with them on a daily bases. Brokerage firms seem to be
able to do repeatable pricing but the tile guys (and their computer
systems) can not.
--
Jeff Barnett
Barry Margolin
2020-03-22 20:53:46 UTC
Permalink
Post by Jeff Barnett
Example 1: Stock prices are reported in 1/8$ amounts. That's 12.5 cent
increments. When a bunch of shares are bought/sold, the bulk price may
have a fractional cent in it.
I think this changed a number of years ago.
--
Barry Margolin, ***@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
Jeff Barnett
2020-03-23 05:15:05 UTC
Permalink
Post by Barry Margolin
Post by Jeff Barnett
Example 1: Stock prices are reported in 1/8$ amounts. That's 12.5 cent
increments. When a bunch of shares are bought/sold, the bulk price may
have a fractional cent in it.
I think this changed a number of years ago.
Well I believe that is what newspapers report. Further, I've seen recent
brokerage reports with numbers of shares specified to 4 (count them)
decimal places! Further, some stocks pay dividends and when that
dividend isn't an even number of pennies, the value of the stock drops
by the exact dividend amount. I've also read about arbitraging computers
making money that is a fraction of a cent per share between two markets.
Those with a "seat" on an exchange can play these games.
--
Jeff Barnett
paul wallich
2020-03-23 14:04:45 UTC
Permalink
Post by Jeff Barnett
Post by Barry Margolin
Post by Jeff Barnett
Example 1: Stock prices are reported in 1/8$ amounts. That's 12.5 cent
increments. When a bunch of shares are bought/sold, the bulk price may
have a fractional cent in it.
I think this changed a number of years ago.
Well I believe that is what newspapers report. Further, I've seen recent
brokerage reports with numbers of shares specified to 4 (count them)
decimal places! Further, some stocks pay dividends and when that
dividend isn't an even number of pennies, the value of the stock drops
by the exact dividend amount. I've also read about arbitraging computers
making money that is a fraction of a cent per share between two markets.
Those with a "seat" on an exchange can play these games.
In the US, I believe that income taxes can still be computed either
rounded to the nearest cent or the nearest dollar. Depending on the
exact composition of your income and expenses, there's probably a tiny
amount to be saved by doing one or the other.

paul
Kaz Kylheku
2020-03-23 14:59:17 UTC
Permalink
Post by paul wallich
Post by Jeff Barnett
Post by Barry Margolin
Post by Jeff Barnett
Example 1: Stock prices are reported in 1/8$ amounts. That's 12.5 cent
increments. When a bunch of shares are bought/sold, the bulk price may
have a fractional cent in it.
I think this changed a number of years ago.
Well I believe that is what newspapers report. Further, I've seen recent
brokerage reports with numbers of shares specified to 4 (count them)
decimal places! Further, some stocks pay dividends and when that
dividend isn't an even number of pennies, the value of the stock drops
by the exact dividend amount. I've also read about arbitraging computers
making money that is a fraction of a cent per share between two markets.
Those with a "seat" on an exchange can play these games.
In the US, I believe that income taxes can still be computed either
rounded to the nearest cent or the nearest dollar. Depending on the
exact composition of your income and expenses, there's probably a tiny
amount to be saved by doing one or the other.
In Canada, the CCRA does not charge or refund tax return balances that
are less than two dollars.

So, a choice of rounding methods that makes a few cents difference to
the bottom line doesn't mean anything.

If the correct value is $1.99 and your rounding method is so off that
it's $1.20, you still don't have to pay anything, or get anything back,
as the case may be.

No reasonable rounding method will cause an error of anywhere near two
dollars on anyone's return, which tells us that if they don't care
about two dollars, they certainly don't care about rounding.

I used to round fractional calculations in ways that were favorable to
me when doing pencil-and-paper returns. E.g. if some tax credit worked
out to 123.4517 dollars, since a credit is favorable to me, I would
round it up to 123.46. Never had that come back as a mistake.
--
TXR Programming Lanuage: http://nongnu.org/txr
Music DIY Mailing List: http://www.kylheku.com/diy
ADA MP-1 Mailing List: http://www.kylheku.com/mp1
Stefan Monnier
2020-03-22 18:41:30 UTC
Permalink
Post by Kaz Kylheku
But suppose you're dealing with currency. Then you have to round every
calculation that is supposed to produce a monetary amount (e.g. dollars
and cents). For instance if some tax calculation produces $123.45678,
you need to make that into $123.46 before entering it into the ledger.
But, AFAIU, you don't want to represent this 123.46 using a floating
point number, because your implementation's choice of floating point
numbers is probably unable to represent 123.46 exactly.

IOW when dealing with such numbers you'll want to use "fixed point"
numbers or fractional numbers so you can have precise control over
rounding errors.


Stefan
Kaz Kylheku
2020-03-23 14:20:08 UTC
Permalink
Post by Stefan Monnier
Post by Kaz Kylheku
But suppose you're dealing with currency. Then you have to round every
calculation that is supposed to produce a monetary amount (e.g. dollars
and cents). For instance if some tax calculation produces $123.45678,
you need to make that into $123.46 before entering it into the ledger.
But, AFAIU, you don't want to represent this 123.46 using a floating
point number, because your implementation's choice of floating point
numbers is probably unable to represent 123.46 exactly.
But that ("don't use floating for currency") is actually just a popular myth;
it can be done.

123.46 is not represented exactly, indeed. But, the difference between
the approximation and 123.46 is vanishingly small. It's like in the
ninth place after the decimal point or something like that.

If you round all the calculations consistently, you will always end up
with a good approximation that is far closer to the correct penny
than to any other penny.

The problem with floating-point is mainly range: if you're
astronomically rich, then you no longer /have/ an approximation for each
penny that is closer to it than to any other penny.

IEEE 64 bit yields 15 digits of precision. A decimal number confined to
15 digits can be converted to a 64 bit double and back without loss of
digits (if it is in range of the type).

15 digits is a lot of dollar, even if we use two of those digits for cents.
You have to be astronomically rich before you have a range-induced
precision problem.
Post by Stefan Monnier
IOW when dealing with such numbers you'll want to use "fixed point"
numbers or fractional numbers so you can have precise control over
rounding errors.
The only situation in which you would want precise rounding control
would be when a specific pencil-and-paper rule is dictated: how you
should round a fractional result to the smallest monetary unit
(such as penny).

That's not related to dealing with rounding error: when we fix $0.12345
to $0.12, we are not fixing an error; we are *introducing* one, a rather
large one, for the sake of pragmatics.

If you have to conform with specific rules about rounding, those will be
based on a decimal representation. You can implement them by printing a
decimal representation of the number into a string, and dealing with the
digits.

For instance, we take 0.123456 and first print it, such that it is
clamped to four digits (using the printing routine's own rounding
method): "0.1235". Now we have a character string, in which a four-digit
fractional part is in a fixed trailing position; we can look at the last
two digits "35" and decide what to do with the "12".
--
TXR Programming Lanuage: http://nongnu.org/txr
Music DIY Mailing List: http://www.kylheku.com/diy
ADA MP-1 Mailing List: http://www.kylheku.com/mp1
Loading...