Округлить разделенное число в Bash

Как мне округлить результат из двух разделенных чисел, например.

3/2

Как когда я делаю

testOne=$((3/2))

$testOne содержит «1», хотя должно было быть округлено до «2», поскольку ответ от 3/2 = 1,5


person Mint    schedule 07.03.2010    source источник
comment
По сути, DUP SO 2394988 (stackoverflow.com/questions/2394988) - тот же плакат.   -  person Jonathan Leffler    schedule 07.03.2010
comment
Конечно, это очень похоже, но я бы не назвал это DUP, но не стесняйтесь закрывать его сейчас, так как я все равно получил свой ответ сейчас, спасибо ghostdog!   -  person Mint    schedule 07.03.2010
comment
Согласованный! Эти два вопроса не совпадают. Один просит потолок. Этот запрашивает округление, которое может округляться вверх или вниз.   -  person shparekh    schedule 16.09.2014


Ответы (10)


Чтобы выполнить округление в арифметике усечения, просто добавьте (denom-1) к числителю.

Пример округления в меньшую сторону:

N/2
M/5
K/16

Пример округления:

(N+1)/2
(M+4)/5
(K+15)/16

Чтобы выполнить округление до ближайшего, добавьте (denom/2) к числителю (половины округляются в большую сторону):

(N+1)/2
(M+2)/5
(K+8)/16
person Ben Voigt    schedule 07.03.2010
comment
Можете ли вы объяснить это немного больше? Что означает деном для одного? И я действительно не понимаю, что делают все эти скобки и буквы…? Я чувствую, что должен знать, так как других ответов не было и 5+ повторений - person Mint; 07.03.2010
comment
@Mint: он показывает обобщенный ответ, используя алгебраическую запись. Используя ваш пример Bash, это будет выглядеть так: testOne=$(( (3 + (2 - 1) / 2)). В более общем смысле, но в синтаксисе Bash это будет что-то вроде answer=$(( ($numerator + ($denominator - 1) / $denomonator)). Вы также можете сделать это таким образом, чтобы исключить все знаки доллара и предоставить больше свободы с пробелами (например, вокруг знака равенства): ((answer = (numerator + (denominator - 1) / denomonator)) - person Dennis Williamson; 07.03.2010
comment
Да, я сделал несколько опечаток в скобках. - person Dennis Williamson; 08.03.2010
comment
@tommy.carstensen: Вы правы. Здесь нет указаний на то, что проблема связана с отрицательными числами (вопросы сценария обычно этого не делают). Аналогичным образом можно обрабатывать и отрицательные числа, но формула быстро усложняется. - person Ben Voigt; 24.01.2014
comment
Неверно testOne=$(( (3 + (2 - 1) / 2)) правильно: testOne=$(( (3 + (2 - 1)) / 2)) - person Pro Backup; 05.03.2017

Хорошее решение - получить ближайшее круглое число

var=2.5
echo $var | awk '{print int($1+0.5)}'

Логика проста, если десятичное значение var меньше 0,5, тогда ближайшее значение является целым числом. Что ж, если десятичное значение больше 0,5, то добавляется следующее целое значение, и, поскольку awk принимает только целую часть. Проблема решена

person Ashish    schedule 12.06.2013
comment
Это было очень полезно, когда я преобразовывал использование памяти из КБ в МБ, так как мне все равно на десятичную часть. - person ashah; 04.04.2017

bash не даст вам правильного результата 3/2, так как он не выполняет математику с плавающей запятой. вы можете использовать такие инструменты, как awk

$ awk  'BEGIN { rounded = sprintf("%.0f", 3/2); print rounded }'
2

or bc

$ printf "%.0f" $(echo "scale=2;3/2" | bc)
2
person ghostdog74    schedule 07.03.2010
comment
awk 'НАЧАЛО { округлено = sprintf(%.0f, 1/2); print rounded}' возвращает 0, а не 1. - person tommy.carstensen; 24.01.2014
comment
Вы должны отметить, что это делает округление объективным. - person Steven Penny; 12.02.2014
comment
Мне нравится этот, так как он более удобочитаем (математические обходные пути хороши, но не удобочитаемы), и мне не нужна точность округления до 0,5. Обратите внимание, что awk 'BEGIN { rounded = sprintf("%.0f", 1.000001/2); print rounded }' возвращает 1 - person Kar.ma; 06.04.2017
comment
Непредвзятое округление означает, что $ echo 4.5 |awk '{printf "%.0f",$0}' дает 4, а $ echo 5.5 |awk '{printf "%.0f",$0}' дает 6 — округляет 0,5 до ближайшего четного целого числа. - person unhammer; 16.11.2018

Если у вас есть целочисленное деление положительных чисел, которое округляется до нуля, то вы можете добавить к делимому на единицу меньше, чем делитель, чтобы округлить его в большую сторону.

То есть заменить X / Y на (X + Y - 1) / Y.

Доказательство:

  • Случай 1: X = k * Y (X кратно Y): в этом случае у нас есть (k * Y + Y - 1) / Y, которое разбивается на (k * Y) / Y + (Y - 1) / Y. Часть (Y - 1)/Y округляется до нуля, и у нас остается частное k. Это именно то, что нам нужно: когда входные данные делятся, мы хотим, чтобы скорректированный расчет по-прежнему давал правильное точное частное.

  • Случай 2: X = k * Y + m, где 0 < m < Y (X не кратно Y). В этом случае у нас есть числитель k * Y + m + Y - 1 или k * Y + Y + m - 1, и мы можем записать деление как (k * Y)/Y + Y/Y + (m - 1)/Y. Так как 0 < m < Y, 0 <= m - 1 < Y - 1, и так последний член (m - 1)/Y стремится к нулю. У нас осталось (k * Y)/Y + Y/Y, которые составляют k + 1. Это показывает, что поведение округляется. Если у нас есть X, которое k кратно Y, если мы добавим к нему всего 1, деление округляется до k + 1.

Но это округление крайне противоположно; все неточные деления уходят от нуля. Как насчет чего-то среднего?

Этого можно добиться, "заполнив" числитель числом Y/2. Вместо X/Y рассчитайте (X+Y/2)/Y. Вместо доказательства, давайте эмпирически:

$ round()
> {
>   echo $((($1 + $2/2) / $2))
> }
$ round 4 10
0
$ round 5 10
1
$ round 6 10
1
$ round 9 10
1
$ round 10 10
1
$ round 14 10
1
$ round 15 10
2

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

Например, round 6 12 переходит в 1, как и все значения, которые равны 6 по модулю 12, например 18 (которое переходит в 2) и так далее. round 5 12 переходит в 0.

Для нечетных чисел поведение правильное. Ни одно из точных рациональных чисел не находится посередине между двумя последовательными кратными. Например, со знаменателем 11 у нас есть 5/11 < 5.5/11 (exact middle) < 6/11; и round 5 11 округляется вниз, тогда как round 6 11 округляется вверх.

person Kaz    schedule 16.06.2014
comment
Это отличное решение, и мне нравится, когда оно объединено в виде функции. Для пояснения, функция round x y берет x, деленное на y, и округляет до ближайшего целого числа. - person Ro Yo Mi; 30.07.2019

Чтобы округлить, вы можете использовать модуль.

Вторая часть уравнения добавит к True, если есть остаток. (Истина = 1; Ложь = 0)

ex: 3/2

answer=$(((3 / 2) + (3 % 2 > 0)))
echo $answer
2

ex: 100 / 2

answer=$(((100 / 2) + (100 % 2 > 0)))
echo $answer
50

ex: 100 / 3

answer=$(((100 / 3) + (100 % 3 > 0)))
echo $answer
34
person sk8asd123    schedule 19.09.2014

Учитывая значение с плавающей запятой, мы можем тривиально округлить его с помощью printf:

# round $1 to $2 decimal places
round() {
    printf "%.{$2:-0}f" "$1"
}

Потом,

# do some math, bc style
math() {
    echo "$*" | bc -l
}

$ echo "Pi, to five decimal places, is $(round $(math "4*a(1)") 5)"
Pi, to five decimal places, is 3.14159

Или, чтобы использовать исходный запрос:

$ echo "3/2, rounded to the nearest integer, is $(round $(math "3/2") 0)"
3/2, rounded to the nearest integer, is 2
person N. Ingersoll    schedule 14.06.2015
comment
printf "%.0f" "0.5" печатает 0 вместо ожидаемого 1, а printf "%.0f" "0.6" печатает 1 как и ожидалось. - person Alexej Magura; 11.11.2016
comment
ИМХО printf "%.2f" "$1" неправильно :printf "%.0f" "2.6": bash: printf: 2.6: invalid number. - person pevik; 18.09.2019
comment
Сделал опечатку. Должно быть printf "%.${2:-0}f" "$1". Я не могу изменить 1 символ :-( - person Marco; 22.01.2020

Если десятичным разделителем является запятая (например: LC_NUMERIC=fr_FR.UTF-8, см. здесь):

$ printf "%.0f" $(echo "scale=2;3/2" | bc) 
bash: printf: 1.50: nombre non valable
0

Замена необходима для решения ghostdog74:

$ printf "%.0f" $(echo "scale=2;3/2" | bc | sed 's/[.]/,/')
2

or

$ printf "%.0f" $(echo "scale=2;3/2" | bc | tr '.' ',')
2
person Q-D    schedule 19.08.2014

Другое решение - выполнить деление внутри команды Python. Например:

$ numerator=90
$ denominator=7
$ python -c "print (round(${numerator}.0 / ${denominator}.0))"

Мне кажется менее архаичным, чем использование awk.

person Sean Staats    schedule 04.05.2012
comment
дополнение: при округлении вниз вы также можете использовать деление пола: print ${числитель} // ${знаменатель} - person Jarno; 29.09.2016
comment
Почему бы не вызвать Java из bash? По словам создателей, Java очень быстр и обладает высокой производительностью арифметических операций. Это конечно шутка, но шутка эта очень грустная, потому что ваш пример такой же смешной, как арифметика Java в bash. - person Dennis V.R.; 21.06.2020

Я думаю, этого должно быть достаточно.

$ echo "3/2" | bc 
person guest    schedule 19.06.2013
comment
Это работает для вашего примера, однако echo "2.5*3" | bc дает 7.5 - person mhwombat; 08.08.2014

Следующее сработало для меня.

 #!/bin/bash
function float() {
bc << EOF
num = $1;
base = num / 1;
if (((num - base) * 10) > 1 )
    base += 1;
print base;
EOF
echo ""
}

float 3.2
person xs2rashid    schedule 02.09.2018