Как использовать деление с плавающей точкой в ​​Bash?

Я пытаюсь разделить две ширины изображения в скрипте Bash, но Bash дает мне 0 в результате:

RESULT=$(($IMG_WIDTH/$IMG2_WIDTH))

Я изучил руководство по Bash и знаю, что должен использовать bcво всех примерах в интернете они используют bc, В echo Я пытался положить то же самое в моем SCALE но это не сработало.

Вот пример, который я нашел в уроках:

echo "scale=2; ${userinput}" | bc 

Как я могу получить Bash, чтобы дать мне как поплавок 0.5?

24 ответа

Решение

Ты не можешь bash делает только целые числа; Вы должны делегировать инструменту, такому как bc,

Вы можете сделать это:

bc <<< 'scale=2; 100/3'
33.33

ОБНОВИТЬ 20130926: ты можешь использовать:

bc -l <<< '100/3' # saves a few hits

удар

Как отмечают другие, bash не поддерживает арифметику с плавающей запятой, хотя вы можете подделать ее с помощью некоторого фиксированного десятичного трюка, например, с двумя десятичными знаками:

echo $(( 100 * 1 / 3 )) | sed 's/..$/.&/'

Выход:

.33

Смотрите ответНильфреда для аналогичного, но более краткого подхода.

альтернативы

Помимо упомянутых bc а также awk Альтернативы есть также следующие:

CLISP

clisp -x '(/ 1.0 3)'

с убранным выходом:

clisp --quiet -x '(/ 1.0 3)'

или через стандартный ввод:

echo '(/ 1.0 3)' | clisp --quiet | tail -n1

Округ Колумбия

echo 2k 1 3 /p | dc

гений кли калькулятор

echo 1/3.0 | genius

Gnuplot

echo 'pr 1/3.' | gnuplot

JQ

echo 1/3 | jq -nf /dev/stdin

Или же:

jq -n 1/3

КШ

echo 'print $(( 1/3. ))' | ksh

Lua

lua -e 'print(1/3)'

или через стандартный ввод:

echo 'print(1/3)' | lua

максима

echo '1/3,numer;' | maxima

с убранным выходом:

echo '1/3,numer;' | maxima --quiet | sed -En '2s/[^ ]+ [^ ]+ +//p'

узел

echo 1/3 | node -p

октава

echo 1/3 | octave

Perl

echo print 1/3 | perl

питон

echo print 1/3. | python

р

echo 1/3 | R --no-save

с убранным выходом:

echo 1/3 | R --vanilla --quiet | sed -n '2s/.* //p'

Рубин

echo print 1/3.0 | ruby

Wcalc

echo 1/3 | wcalc

С убранным выходом:

echo 1/3 | wcalc | tr -d ' ' | cut -d= -f2

ЗШ

echo 'print $(( 1/3. ))' | zsh

Другие источники

Стефан Шазелас ответил на аналогичный вопрос в Unix.SX.

Немного улучшив ответ Марвина:

RESULT=$(awk "BEGIN {printf \"%.2f\",${IMG_WIDTH}/${IMG2_WIDTH}}")

bc не всегда поставляется как установленный пакет.

Вы можете использовать bc -l вариант (буква L)

RESULT=$(echo "$IMG_WIDTH/$IMG2_WIDTH" | bc -l)

В качестве альтернативы bc вы можете использовать awk в вашем скрипте.

Например:

echo "$IMG_WIDTH $IMG2_WIDTH" | awk '{printf "%.2f \n", $1/$2}'

Выше " %.2f " сообщает функции printf возвращать число с плавающей запятой с двумя цифрами после запятой. Я использовал echo для передачи переменных как полей, так как awk работает с ними корректно. " $1 " и " $2 " относятся к первому и второму полям, вводимым в awk.

И вы можете сохранить результат как некоторую другую переменную, используя:

RESULT = `echo ...`

Что ж, до float было время, когда использовалась логика с фиксированными десятичными числами:

IMG_WIDTH=100
IMG2_WIDTH=3
RESULT=$((${IMG_WIDTH}00/$IMG2_WIDTH))
echo "${RESULT:0:-2}.${RESULT: -2}"
33.33

Последняя строка - bashim, если не используете bash, попробуйте этот код:

IMG_WIDTH=100
IMG2_WIDTH=3
INTEGER=$(($IMG_WIDTH/$IMG2_WIDTH))
DECIMAL=$(tail -c 3 <<< $((${IMG_WIDTH}00/$IMG2_WIDTH)))
RESULT=$INTEGER.$DECIMAL
echo $RESULT
33.33

Логическое обоснование кода: умножить на 100 перед делением, чтобы получить 2 десятичных знака.

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

% IMG_WIDTH=1080
% IMG2_WIDTH=640
% result=$((IMG_WIDTH*1.0/IMG2_WIDTH))
% echo $result
1.6875

Этот пост может вам помочь: bash - Стоит ли переходить на zsh для повседневного использования?

Как выполнять вычисления с плавающей запятой в bash:

Вместо использования " здесь строк" (<<<) с bc, как и в одном из самых популярных примеров, вот мой любимыйbc пример с плавающей запятой, прямо из EXAMPLES раздел bc страницы руководства (см. man bc для страниц руководства).

Прежде чем мы начнем, знайте, что уравнение для числа Пи: pi = 4*atan(1). a() ниже bc математическая функция для atan().

  1. Вот как сохранить результат вычисления с плавающей запятой в переменной bash - в данном случае в переменной с именемpi. Обратите внимание, чтоscale=10в этом случае устанавливает количество десятичных цифр точности равным 10. Все десятичные цифры после этого места обрезаются.

    pi=$(echo "scale=10; 4*a(1)" | bc -l)
    
  2. Теперь, чтобы иметь одну строку кода, которая также выводит значение этой переменной, просто добавьте echoдо конца в качестве последующей команды, как показано ниже. Обратите внимание на усечение до 10 знаков после запятой, как указано:

    pi=$(echo "scale=10; 4*a(1)" | bc -l); echo $pi
    3.1415926532
    
  3. Наконец, давайте немного округлим. Здесь мы будем использоватьprintfфункция округления до 4 знаков после запятой. Обратите внимание, что3.14159... раундов сейчас 3.1416. Поскольку мы округляем, нам больше не нужно использоватьscale=10чтобы усечь до 10 знаков после запятой, поэтому мы просто удалим эту часть. Вот и конечное решение:

    pi=$(printf %.4f $(echo "4*a(1)" | bc -l)); echo $pi
    3.1416
    

Вот еще одно действительно отличное приложение и демонстрация вышеперечисленных методов: измерение и печать времени выполнения.

Обратите внимание, что dt_min округляется от 0.01666666666... к 0.017:

start=$SECONDS; sleep 1; end=$SECONDS; dt_sec=$(( end - start )); dt_min=$(printf %.3f $(echo "$dt_sec/60" | bc -l)); echo "dt_sec = $dt_sec; dt_min = $dt_min"
dt_sec = 1; dt_min = 0.017

Связанные с:

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

Здесь я оборачиваю некоторый bashism в функцию div:

Один лайнер:

function div { if [ "" == "$3" ]; then local _d=2; else local _d=$3; fi; local _n=0000000000; _n=${_n:0:$_d}; local _r=$(($1$_n/$2)); _r=${_r:0:-$_d}.${_r: -$_d}; echo $_r;}

Или многострочный:

function div {
  if [ "" == "$3" ]; then
    local _d=2
  else
    local _d=$3
  fi
  local _n=0000000000
  _n=${_n:0:$_d}
  local _r=$(($1$_n/$2))
  _r=${_r:0:-$_d}.${_r: -$_d}
  echo $_r
}

Теперь у вас есть функция

div <dividend> <divisor> [<precision=2>]

и использовать его как

> div 1 2
.50

> div 273 123 5
2.21951

> x=$(div 22 7)
> echo $x
3.14

Я знаю, что он старый, но слишком заманчивый. Итак, ответ: вы не можете... но вы вроде как можете. давайте попробуем это:

$IMG_WIDTH=1024
$IMG2_WIDTH=2048

$RATIO="$(( IMG_WIDTH / $IMG2_WIDTH )).$(( (IMG_WIDTH * 100 / IMG2_WIDTH) % 100 ))

таким образом, вы получаете 2 цифры после точки, усеченные (назовите это округлением в меньшую сторону, ха-ха) в чистом bash (нет необходимости запускать другие процессы). конечно, если вам нужна только одна цифра после точки, вы умножаете на 10 и делаете по модулю 10.

что это делает:

  • первый $((...)) выполняет целочисленное деление;
  • second $((...)) выполняет целочисленное деление на что-то в 100 раз больше, по существу перемещая ваши 2 цифры влево от точки, а затем (%) дает вам только эти 2 цифры, выполняя по модулю.

Бонусный трек: версия bc x 1000 заняла 1,8 секунды на моем ноутбуке, а версия чистого bash - 0,016 секунды.

Для тех, кто пытается вычислить проценты с принятым ответом, но теряет точность:

Если вы запустите это:

echo "scale=2; (100/180) * 180" | bc

Вы получаете 99.00 только, что теряет точность.

Если вы запустите его так:

echo "result = (100/180) * 180; scale=2; result / 1" | bc -l

Теперь ты получаешь 99.99.

Потому что вы масштабируете только в момент печати.

Обратитесь сюда

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

#!/bin/bash

n=$1
d=$2

# because of rounding this should be 10^{i+1}
# where i is the number of decimal digits wanted
i=4
P=$((10**(i+1)))
Pn=$(($P / 10))
# here we 'fix' the decimal place, divide and round tward zero
t=$(($n * $P / $d + ($n < 0 ? -5 : 5)))
# then we print the number by dividing off the interger part and
# using the modulo operator (after removing the rounding digit) to get the factional part.
printf "%d.%0${i}d\n" $(($t / $P)) $(((t < 0 ? -t : t) / 10 % $Pn))

Существуют сценарии, в которых вы не можете использовать bc, потому что он может просто не присутствовать, как в некоторых урезанных версиях busybox или встроенных системах. В любом случае ограничение внешних зависимостей - это всегда хорошая вещь, поэтому вы всегда можете добавить нули к числу, деленному на (числитель), что аналогично умножению на степень 10 (вы должны выбрать степень 10 в соответствии с точность, которая вам нужна), что сделает вывод деление целым числом. Получив это целое число, обработайте его как строку и поместите десятичную точку (перемещая ее справа налево) число раз, равное десятичной степени, на которую вы умножили числитель. Это простой способ получения результатов с плавающей запятой, используя только целые числа.

Это не совсем с плавающей точкой, но если вы хотите что-то, что устанавливает более одного результата в одном вызове bc...

source /dev/stdin <<<$(bc <<< '
d='$1'*3.1415926535897932384626433832795*2
print "d=",d,"\n"
a='$1'*'$1'*3.1415926535897932384626433832795
print "a=",a,"\n"
')

echo bc radius:$1 area:$a diameter:$d

вычисляет площадь и диаметр круга, радиус которого указан в $1

Используйте calc. Это самый простой пример, который я нашел:

расчет 1+1

 2

расчет 1/10

 0.1

Bash может вычислять результаты с плавающей запятой без каких-либо других программ.

Bash самостоятельно может даже точно вычислить π с точностью до девятого знака после запятой.

Пример:

      calc=104348/33215

accuracy=9

calc99p9=$((10**$accuracy))*$calc
result99p9=$((calc99p9))
result=${result99p9: -${#result99p9}: -$accuracy}.${result99p9: -$accuracy}

echo Bash calculated pi to be $result

приводит к

      Bash calculated pi to be 3.141592653

** Безопасная для инъекций математика с плавающей запятой в bash/shell **

Примечание. В центре внимания этого ответа находятся идеи безопасного для инъекций решения для выполнения математических операций в bash (или других оболочках). Конечно, то же самое можно использовать с небольшими настройками для выполнения расширенной обработки строк и т. Д.

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

Ниже приведено сравнение использования различных языков для выполнения основных математических вычислений, где результат отображается с плавающей запятой. Он вычисляет A + B * 0,1 (как с плавающей запятой).

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

Исключением является 'bc', который не обеспечивает ввода / вывода, все данные поступают через программы в stdin, а весь вывод идет на stdout. Все вычисления выполняются в песочнице, что не допускает побочных эффектов (открытие файлов и т. Д.). Теоретически инъекция безопасна по своей конструкции!

A=5.2
B=4.3

# Awk: Map variable into awk
# Exit 0 (or just exit) for success, non-zero for error.
#
awk -v A="$A" -v B="$B" 'BEGIN { print A + B * 0.1 ; exit 0}'

# Perl
perl -e '($A,$B) = @ARGV ; print $A + $B * 0.1' "$A" "$B"

# Python 2
python -c 'import sys ; a = float(sys.argv[1]) ; b = float(sys.argv[2]) ; print a+b*0.1' "$A" "$B"

# Python 3
python3 -c 'import sys ; a = float(sys.argv[1]) ; b = float(sys.argv[2]) ; print(a+b*0.1)' "$A" "$B"

# BC
bc <<< "scale=1 ; $A + $B * 0.1"

Как указывали другие, bash не имеет встроенных операторов с плавающей запятой.

Вы можете реализовать плавающую точку в bash без использования программ-калькуляторов, таких как bc и awk (или любых внешних программ, если на то пошло).

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

Любые отзывы приветствуются!

Дивиденд = делитель × частное + остаток

Давайте просто посчитаем частное и остаток. А также объединить эти строки в переменную.

      bar=1234 \
&& divisor=1000 \
    && foo=$(printf "%s.%s" $(( bar / divisor )) $(( bar % divisor ))) \
    && printf "bar is %d miliseconds or %s seconds\n" $bar $foo

Выход: bar is 1234 miliseconds or 1.234 seconds

Вот команда awk: -F = разделитель полей == +

echo "2.1+3.1" |  awk -F "+" '{print ($1+$2)}'

насколько точным должен быть вывод? если приближение с помощью биннинга уже приемлемо для вашего варианта использования, вы можете пойти еще дальше и воспользоваться кодами выхода POSIX [0:256) (все остальные целые числа возвращаются в этот диапазон).

например: в gawk / nawk / mawk-1 он уже дает мне эпохальные секунды до целочисленного уровня, но я хотел расширить это, чтобы получить точность, близкую к миллисекундам, но не слишком педантично, я запускаю эту команду в оболочке POSIX

      exit $(( 10#` gdate +%5N ` * 256 / 100000 ))

прямое присвоение 5-значного целого числа, представляющего вывод gnu-date в 1 из 256 ячеек, а затем отмену этого как только awk получит код выхода вызов, т.е. выбранный номер бункера. Я обнаружил, что у этого подхода меньше накладных расходов, чем при использовании полного вызова getline.

Этот метод также напрямую записывает вывод в код выхода POSIX вместо дополнительной распечатки терминала.

(арифметика оболочки если так написано вместо * 0,0256). Если собрать воедино функцию awk, это будет напоминать это. В состоит в том, чтобы заставить основание-10 не допускать интерпретации оболочки posix "01733" как восьмеричного числа.

      function msecs() {     # n x 2**-8 = n divided by 256

    return 2^-8 * \
           system( "exit \44\50\50 "      \
                   " 10\43\140 gdate \53"  \
                   "%5N\140 \52 " \
                   "256 \57 100000 \51\51" )
}
  • для моего собственного кода я применяю еще 0,6% стрижку, чтобы учесть накладные расходы на оболочку.

Вы можете использовать питон

      RESULT=$(python -c "print($IMG_WIDTH/$IMG2_WIDTH)")

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

      declare -a arr
echo "How many numbers you want to enter?"
read n
echo "Enter the Array Elements"
for(( i=0 ; i<$n ; i++))
do
      read array_elements
      arr[$i]="$array_elements"
done
sum=0
for i in "${arr[@]}"
do
     #sum and avg
     sum=$(($sum + $i))
     #average will come in decimals
     avg=`echo $sum / $n | bc -l`
done
# Output results:
printf "Average of Array Elements %.2f:" $avg

Поэтому мы будем использовать " | bc -l " для вычислений с плавающей запятой.

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