Округление результата с помощью dc (настольный калькулятор)

Можно ли округлить вершину стека, используя dc в скрипте оболочки? Если да, есть ли команды для округления результата вверх и вниз?

Спасибо, парни!

2 ответа

Решение

dc Руководство говорит:

На большинство арифметических операций влияет "значение точности", которое можно установить с помощью k команда. Значение точности по умолчанию равно нулю...

/ Выдвигает два значения, делит второе, извлеченное из первого, и выталкивает результат. Количество цифр дроби определяется значением точности.

Таким образом, вы можете усечь (округлить до нуля), используя 0k1/, или просто 1/ если вы знаете, что точность равна 0, то это по умолчанию. Например:

$ dc -e '12.9 1/ p'
12
$ dc -e '_12.9 1/ p'
-12

Другие виды округления сложнее. Для округления до ближайшего целого числа вы можете использовать [_1*]sad.5r0>a+0k1/, например:

$ dc -e '12.9 [_1*]sad.5r0>a+0k1/ p'
13
$ dc -e '_12.9 [_1*]sad.5r0>a+0k1/ p'
-13

Быстрое объяснение:

  1. [_1*]sa сохраняет команду _1* (умножить на -1) в регистре a,
  2. d дублирует значение в верхней части стека (значение, которое мы хотим округлить, назовем его v).
  3. .5r толкает 0,5, а затем меняет два верхних значения, поэтому стек теперь равен v 0,5 v.
  4. 0>a выполняет команду в реестре a если 0> v (то есть, если v отрицательно). Стек теперь равен 0,5 v, если v положительно, или -0.5 v, если v отрицательно.
  5. + добавляет два верхних значения и толкает v + 0.5, если v положительно, или v - 0.5, если v отрицательно.
  6. 0k1/ усечения, как описано выше.

Если вы знаете, что число, которое вы округляете, неотрицательно, вы можете просто использовать .5+0k1/; и если вы дополнительно знаете, что точность равна 0, вы можете использовать .5+1/,

Чтобы округлить, используйте [dX[1-]sa0<a]sad0>a0k1/,

Чтобы округлить, используйте [dX[1+]sa0<a]sad0<a0k1/,

Все эти предложения используют реестр a, так что вам может понадобиться настроить их в вашей реальной программе.

Основываясь на ответе Гарета, используйте следующее для округления банкира (т.е. округление до ближайшего четного целого числа):[_1*]sa[d1r0>a+]sbd0k1/2%0!=b1/,

Обратите внимание, что это использует дополнительный регистр, б.

Это немного плотно, поэтому давайте разберемся с этим:

[_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).
Другие вопросы по тегам