Is it possible to round the top of the stack using dc in a shell skript? If yes are there commands to round the result up and down?
Thanks guys!
Is it possible to round the top of the stack using dc in a shell skript? If yes are there commands to round the result up and down?
Thanks guys!
The dc
manual says:
Most arithmetic operations are affected by the "precision value", which you can set with the
k
command. The default precision value is zero ...
/
Pops two values, divides the second one popped from the first one popped, and pushes the result. The number of fraction digits is specified by the precision value.
So you can truncate (round toward zero) using 0k1/
, or just 1/
if you know that the precision is 0, which it is by default. For example:
$ dc -e '12.9 1/ p'
12
$ dc -e '_12.9 1/ p'
-12
Other kinds of rounding are more difficult. To round to the nearest integer, you can use [_1*]sad.5r0>a+0k1/
, for example:
$ dc -e '12.9 [_1*]sad.5r0>a+0k1/ p'
13
$ dc -e '_12.9 [_1*]sad.5r0>a+0k1/ p'
-13
A quick explanation:
[_1*]sa
stores the command _1*
(multiply by −1) in register a
.d
duplicates the value on top of the stack (the value we want to round, call it v)..5r
pushes 0.5 and then swaps the top two values, so the stack is now v 0.5 v.0>a
executes the command in register a
if 0 > v (that is, if v is negative). The stack is now 0.5 v if v is positive, or −0.5 v if v is negative.+
adds the top two values and pushes v + 0.5 if v is positive, or v − 0.5 if v is negative.0k1/
truncates as described above.If you know that the number you are rounding is non-negative, you can just use .5+0k1/
; and if you additionally know the precision is 0 you can use .5+1/
.
To round down, use [dX[1-]sa0<a]sad0>a0k1/
.
To round up, use [dX[1+]sa0<a]sad0<a0k1/
.
All these suggestions use the register a
, so you might need to adjust them in your actual program.
Building on Gareth's answer, use the following for banker's rounding (i.e. round to the nearest even integer):[_1*]sa[d1r0>a+]sbd0k1/2%0!=b1/
.
Note this uses additional register, b.
That's a bit dense, so let's break it down:
[_1*]sa #1- Assign a macro "$input *= -1" to register 'a'
[d1r0>a+]sb #2- Assign a macro "if($input<0){$input -= 1} else{$input += 1}"
# to register 'b'
d #3- Duplicate the input value; the duplicate will be used
# to decide if the input is odd or even.
0k #4- Set precision to 0; this makes the 1/ operation truncate
# all digits to the right of the decimal point.
1/ #5- Truncate those decimal places on our duplicate input.
2% #6- Modulo 2; if we get 0, it was even, otherwise it's odd.
# Note this consumes the duplicate from line #3.
0!=b #7- If it was odd, run the macro in register 'b'. This adds +/-1
# to the input, respectively if it's positive or negative.
1/ #8- Truncate decimal places; if the input was odd we're now
# at floor($input+1) for positive or floor($input-1)
# for negative; if it was even we're at floor($input).
© 2022 - 2025 — McMap. All rights reserved.