Как округлить десятичные числа с помощью bc в bash?

34

Быстрый пример того, что я хочу использовать сценарии bash:

#!/bin/bash
echo "Insert the price you want to calculate:"
read float
echo "This is the price without taxes:"
echo "scale=2; $float/1.18" |bc -l
read -p "Press any key to continue..."
bash scriptname.sh

Предполагая, что цена составляет: 48.86 Ответ будет следующим: 41.406779661 (41.40 фактически, потому что я использую scale=2; )

Мой вопрос: Как я обойду второй десяток, чтобы показать ответ таким образом ?: 41.41

    
задан blackedx 24.08.2012 в 16:25
источник

12 ответов

28

Функция раунда bash:

round()
{
echo $(printf %.$2f $(echo "scale=$2;(((10^$2)*$1)+0.5)/(10^$2)" | bc))
};

Используется в вашем примере кода:

#!/bin/bash
# the function "round()" was taken from 
# http://stempell.com/2009/08/rechnen-in-bash/

# the round function:
round()
{
echo $(printf %.$2f $(echo "scale=$2;(((10^$2)*$1)+0.5)/(10^$2)" | bc))
};

echo "Insert the price you want to calculate:"
read float
echo "This is the price without taxes:"
#echo "scale=2; $float/1.18" |bc -l
echo $(round $float/1.18 2);
read -p "Press any key to continue..."

Удачи: o)

    
ответ дан user85321 24.08.2012 в 18:24
источник
24

Самое простое решение:

printf %.2f $(echo "$float/1.18" | bc -l)
    
ответ дан migas 16.01.2015 в 17:39
17

Округление Bash / awk:

echo "23.49" | awk '{printf("%d\n",$1 + 0.5)}'  

Если у вас есть python, вы можете использовать что-то вроде этого:

echo "4.678923" | python -c "print round(float(raw_input()))"
    
ответ дан zuberuber 24.08.2012 в 16:28
3

Вот чисто bc-решение. Правила округления: при +/- 0,5, округляются от нуля.

Поместите масштаб, который вы ищете, в $ result_scale; ваша математика должна быть там, где $ MATH находится в списке команд bc:

bc <<MATH
h=0
scale=0

/* the magnitude of the result scale */
t=(10 ^ $result_scale)

/* work with an extra digit */
scale=$result_scale + 1

/* your math into var: m */
m=($MATH)

/* rounding and output */
if (m < 0) h=-0.5
if (m > 0) h=0.5

a=(m * t + h)

scale=$result_scale
a / t
MATH
    
ответ дан MetroEast 26.08.2015 в 21:11
2

Я знаю, что это старый вопрос, но у меня есть чистое «bc'-решение без« if »или ветвей:

#!/bin/sh
bcr()
{
    echo "scale=$2+1;t=$1;scale-=1;(t*10^scale+((t>0)-(t<0))/2)/10^scale" | bc -l
}

Используйте его как bcr '2/3' 5 или bcr '0.666666' 2 - > (выражение, за которым следует масштаб)

Это возможно, потому что в bc (например, C / C ++) разрешено смешивать логические выражения в ваших вычислениях. Выражение ((t>0)-(t<0))/2) будет оцениваться до +/- 0,5 в зависимости от знака 't' и, следовательно, использовать правильное значение для округления.

    
ответ дан Marcus Kolenda 29.04.2017 в 01:59
1

Чистая реализация bc по запросу

define ceil(x) { auto os,xx;x=-x;os=scale;scale=0 xx=x/1;if(xx>x).=xx-- scale=os;return(-xx) }

, если вы поместите это в файл с именем functions.bc, вы можете округлить с помощью

echo 'ceil(3.1415)' | bc functions.bc

Код для реализации bc, найденный на Ссылка

    
ответ дан Aethalides 16.04.2018 в 21:14
1
#!/bin/bash
# - loosely based on the function "round()", taken from 
# http://stempell.com/2009/08/rechnen-in-bash/

# - inspired by user85321 @ askubuntu.com (original author)
#   and Aquarius Power

# the round function (alternate approach):

round2()
{
    v=$1
    vorig=$v
    # if negative, negate value ...
    (( $(bc <<<"$v < 0") == 1 )) && v=$(bc <<<"$v * -1")
    r=$(bc <<<"scale=$3;(((10^$3)*$v/$2)+0.5)/(10^$3)")

    # ... however, since value was only negated to get correct rounding, we 
    # have to add the minus sign again for the resulting value ...

    (( $(bc <<< "$vorig < 0") == 1 )) && r=$(bc <<< "$r * -1")
    env printf %.$3f $r
};

echo "Insert the price you want to calculate:"
read float
echo "This is the price without taxes:"
round2 $float 1.18 2
echo && read -p "Press any key to continue..."

Это на самом деле просто: нет необходимости явно добавлять жесткий код «-0,5» для отрицательных чисел. Математически говоря, мы просто вычислим абсолютное значение аргумента и еще добавим 0.5, как обычно. Но поскольку у нас (к сожалению) нет встроенной функции abs() (если мы не закодируем ее), мы просто отрицаем аргумент, если он отрицательный.

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

    
ответ дан syntaxerror 24.10.2014 в 01:56
1

Я все еще ищу чистый bc ответ на вопрос, как округлить только одно значение внутри функции, но вот чистый bash ответ:

#!/bin/bash

echo "Insert the price you want to calculate:"
read float
echo "This is the price without taxes:"

embiggen() {
  local int precision fraction=""
  if [ "$1" != "${1#*.}" ]; then  # there is a decimal point
    fraction="${1#*.}"       # just the digits after the dot
  fi
  int="${1%.*}"              # the float as a truncated integer
  precision="${#fraction}"   # the number of fractional digits
  echo $(( 10**10 * $int$fraction / 10**$precision ))
}

# round down if negative
if [ "$float" != "${float#-}" ]
  then round="-5000000000"
  else round="5000000000"
fi

# calculate rounded answer (sans decimal point)
answer=$(( ( 'embiggen $float' * 100 + $round ) / 'embiggen 1.18' ))

int=${answer%??}  # the answer as a truncated integer

echo $int.${answer#$int}  # reassemble with correct precision

read -p "Press any key to continue..."

В основном, это тщательно извлекает десятичные числа, умножает все на 100 миллиардов (10¹⁰, 10**10 в bash ), настраивается для точности и округления, выполняет фактическое деление, делит обратно на соответствующую величину и затем повторно вставляет десятичное.

Шаг за шагом:

Функция embiggen() назначает усеченную целочисленную форму своего аргумента на $int и сохраняет числа после точки в $fraction . Количество дробных цифр отмечено в $precision . Математика умножает 10¹⁰ на конкатенацию $int и $fraction , а затем корректирует ее, чтобы соответствовать точности (например, embiggen 48.86 становится 10¹⁰ × 4886/100 и возвращает 488600000000 , что составляет 488 600 000 000).

Мы хотим получить окончательную точность сотых, поэтому умножим первое число на 100, добавим 5 для округления, а затем разделим второе число. Это присвоение $answer оставляет нас в сто раз окончательный ответ.

Теперь нам нужно добавить десятичную точку. Мы назначаем новое $int значение $answer , исключая его последние две цифры, а затем echo с точкой и $answer , исключая значение $int , которое уже позабочено. (Не обращайте внимания на ошибку подсветки синтаксиса, которая делает это похожим на комментарий)

(Башизм: возведение в степень не является POSIX, так что это базис. Чистое решение POSIX потребует, чтобы циклы добавляли нули, а не использовали десять. Кроме того, «embiggen» - это совершенно кромовое слово.)

Одна из основных причин, по которой я использую zsh в качестве моей оболочки, заключается в том, что она поддерживает математику с плавающей запятой. Решение этого вопроса довольно просто в zsh :

printf %.2f $((float/1.18))

(Мне бы хотелось, чтобы кто-то добавил комментарий к этому ответу с трюком, чтобы включить арифметику с плавающей запятой в bash , но я уверен, что такая функция еще не существует.)

    
ответ дан Adam Katz 22.05.2015 в 02:35
1

, если у вас есть результат, например, рассмотрите 2.3747888

все, что вам нужно сделать, это:

d=$(echo "(2.3747888+0.5)/1" | bc); echo $d

это правильно округляет число Пример:

(2.49999 + 0.5)/1 = 2.99999 

десятичные знаки удаляются bc и поэтому округляются до 2, как и должно быть

    
ответ дан user525061 31.03.2016 в 12:24
1

Мне пришлось рассчитать общую продолжительность коллекции аудиофайлов.

Поэтому мне пришлось:

а. получить продолжительность для каждого файла ( не показано )

В. (все они были в NNN.NNNNNN (fp) секунд)

С. отдельные часы, минуты, секунды, секунды.

Д. выведите строку HR: MIN: SEC: FRAMES, где frame = 1/75 с.

(Фреймы поступают из кода SMPTE, используемого в студиях.)

A: используйте ffprobe и строку длительности разбора в число fp (не показано)

В:

 # add them up as a series of strings separated by "+" and send it to bc

arr=( "${total[@]}" )  # copy array

# IFS is "Internal Field Separator"
# the * in arr[*] means "all of arr separated by IFS" 
# must have been made for this
IFS='+' sum=$(echo "scale=3; ${arr[*]} "| bc -l)# (-l= libmath for fp)
echo $sum 

С

# subtract each amount of time from tt and store it    
tt=$sum   # tt is a running var (fp)


hrs=$(echo "$tt / 3600" | bc)

tt=$(echo "$tt - ( $hrs * 3600 )" | bc )

min=$(echo "$tt / 60" | bc )

tt=$(echo "$tt - ($min *60)" | bc )

sec=$(echo "$tt/1" | bc )

tt=$(echo "$tt - $sec" | bc )

frames=$(echo "$tt * 75"  | bc ) # 75 frames /sec 
frames=$(echo "$frames/1" | bc ) # truncate to whole #

D:

#convert to proper format with printf (bash builtin)        
hrs=$(printf "%02d\n" $hrs)  # format 1 -> 01 

min=$(printf "%02d\n" $min)

sec=$(printf "%02d\n" $sec)

frames=$(printf "%02d\n" $frames)

timecode="$hrs:$min:$sec:$frames"

# timecode "01:13:34:54"
    
ответ дан Chris Reid 08.06.2017 в 02:31
1

Ниже приведена сокращенная версия вашего скрипта, исправленная для обеспечения требуемого результата:

#!/bin/bash
float=48.86
echo "You asked for $float; This is the price without taxes:"
echo "scale=3; price=$float/1.18 +.005; scale=2; price/1 " | bc

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

Кроме того, масштабный коэффициент применяется во время работы; так (это команды bc , вы можете вставить их в свой терминал):

float=48.86; rate=1.18; 
scale=2; p2=float/rate
scale=3; p3=float/rate
scale=4; p4=float/rate
print "Compare:  ",p2, " v ", p3, " v ", p4
Compare:  41.40 v 41.406 v 41.4067

# however, scale does not affect an entered value (nor addition)
scale=0
a=.005
9/10
0
9/10+a
.005

# let's try rounding
scale=2
p2+a
41.405
p3+a
41.411
(p2+a)/1
41.40
(p3+a)/1
41.41
    
ответ дан toddkaufmann 27.11.2017 в 09:35
0
bc() {
        while :
        do
                while IFS='$\n' read i
                do
                        /usr/bin/bc <<< "scale=2; $i" | sed 's/^\./0&/'
                done
        done
}
    
ответ дан sjas 13.10.2018 в 14:40