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

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

1
задан 24 August 2012 в 19:31

10 ответов

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

printf %.2f $(echo "$float/1.18" | bc -l)
23
ответ дан 25 May 2018 в 07:13
  • 1
    Как отмечалось также в комментарии выше ( askubuntu.com/a/179949/512213 ), к сожалению, это ведет себя неправильно для отрицательных чисел. – RobertG 2 March 2016 в 20:35
  • 2
    @RobertG: Почему? Это решение не использует +0.5. Попробуйте printf "%.2f\n" "$(bc -l <<<"48.86/1.18")" "$(bc -l <<<"-48.86/1.18")", и вы получите 41.41 и -41.41. – musiphil 12 August 2016 в 20:20
  • 3
    Также возможно использование трубы: echo "$float/1.18" | bc -l | xargs printf %.2f – dessert 4 November 2017 в 14:05

Bash / awk rounding:

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

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

echo "4.678923" | python -c "print round(float(raw_input()))"
17
ответ дан 25 May 2018 в 07:13
  • 1
    Спасибо за совет, но он не решает, что мне нужно. Я должен сделать это, просто используя bash ... – blackedx 24 August 2012 в 19:39
  • 2
    Команда python более читаема и хороша для быстрых скриптов. Также поддерживает произвольное округление цифр путем добавления, например, & quot ;, 3 " к круглой функции. благодаря – Jordan Trudgett 13 November 2013 в 08:07
  • 3
    echo "$float" |awk '{printf "%.2f", $1/1.18}' выполнит запрошенную математику вопроса до требуемого значения сотых долей. Это так много "с использованием bash" как вызов bc в вопросе. – Adam Katz 22 May 2015 в 01:41
  • 4
    Для Python вы можете просто использовать что-то вроде python -c "print(round($num))", где num=4.678923. Не нужно гадоваться с stdin. Вы также можете округлить до n цифр следующим образом: python -c "print(round($num, $n))". – Six 26 November 2015 в 00:32
  • 5
    Мне удалось сделать что-то довольно аккуратное с этим решением, моя проблема была 0,5, в этом случае я не всегда получал необходимый раунд, поэтому я придумал следующее: echo 5 | python -c "print int(round((float(raw_input())/2)-0.5))" (Когда + округляется и заканчивается ). – Yaron 13 June 2016 в 12:39

Вот чисто 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
2
ответ дан 25 May 2018 в 07:13
  • 1
    очень интересно! MATH=-0.34;result_scale=1;bc <<MATH, #ObjectiveAndClear: 5 как объяснено здесь :) – Aquarius Power 3 March 2016 в 00:42

Я знаю, что это старый вопрос, но у меня есть чистое «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» и, следовательно, использовать правильное значение для округления.

2
ответ дан 25 May 2018 в 07:13
  • 1
    Это выглядит аккуратно, но bcr "5 / 2" 0 возвращает 2 вместо 3. Я что-то упускаю? – blujay 3 January 2018 в 12:25
#!/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() в нашем распоряжении (если мы не будем ее кодировать), мы просто отрицаем аргумент, если он отрицательный.

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

1
ответ дан 25 May 2018 в 07:13
  • 1
    @muru о ваших последних изменениях: herestrings вместо echo .. |, арифметика оболочки, какая точка echo $()? Это легко объяснить: ваши строки здесь всегда потребуют для записи /tmp каталог! И мое решение также будет работать в среде только для чтения, например. г. аварийная корневая оболочка, где / - , а не всегда записывается по умолчанию. Поэтому была хорошая причина, почему я так закодировал ее. – syntaxerror 17 April 2015 в 17:48
  • 2
    Я согласен с этим. Но когда echo $() когда-либо понадобилось? Это и отступы подсказали мои правки, только что произошло. – muru 17 April 2015 в 17:54
  • 3
    @muru Ну, самый бессмысленный случай echo $(), который был запоздалым, чтобы быть исправленным, на самом деле предпоследняя линия, lol. Спасибо за хедз-ап. По крайней мере, сейчас это выглядит намного лучше, я не могу отрицать. В любом случае, мне нравилась логика в этом. Множество булевых материалов, менее избыточных "if" (которые наверняка взорвут код на 50 процентов). – syntaxerror 17 April 2015 в 17:58

Я все еще ищу чистый 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), настраивается для точности и округления, выполняет фактическое деление, делит обратно на соответствующую величину и затем вставляет десятичное число.

[d2 ] Шаг за шагом:

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

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

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

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

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

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

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

1
ответ дан 25 May 2018 в 07:13

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

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

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

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

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

1
ответ дан 25 May 2018 в 07:13

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

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

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

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

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

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

не показано

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

B:

 # 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 

C:

# 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"
1
ответ дан 25 May 2018 в 07:13

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

#!/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
1
ответ дан 25 May 2018 в 07:13

Чистая реализация 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, то вы можете округлить с помощью

[ f2]

Код для реализации bc найден на http://phodd.net/gnu-bc/code/funcs.bc

1
ответ дан 25 May 2018 в 07:13

Другие вопросы по тегам:

Похожие вопросы: