Как использовать деление с плавающей запятой в bash?

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

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

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

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

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

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

Ответ 1

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

Ответ 2

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

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

UPDATE 20130926: вы можете использовать:

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

Ответ 3

удар

Как отмечали другие, 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

python2

echo print 1/3. | python2

python3

echo 'print(1/3)' | python3

р

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

единицы

units 1/3

С компактным выходом:

units --co 1/3

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

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

Ответ 4

Несколько улучшился ответ Марвина:

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

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

Ответ 5

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

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

Ответ 6

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

Например:

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

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

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

RESULT = `echo ...`

Ответ 7

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

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

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

Ответ 8

Хорошо, до того, как 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 десятичных знака.

Ответ 9

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

Ответ 10

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

Ответ 11

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

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

Один лайнер:

function div { local _d=${3:-2}; local _n=0000000000; _n=${_n:0:$_d}; local _r=$(($1$_n/$2)); _r=${_r:0:-$_d}.${_r: -$_d}; echo $_r;}

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

function div {
  local _d=${3:-2}
  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

Ответ 12

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

$IMG_WIDTH=1024
$IMG2_WIDTH=2048

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

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

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

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

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

Ответ 13

Хотя вы не можете использовать деление с плавающей запятой в 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))

Ответ 14

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

рассчитать 1 + 1

 2

расчет 1/10

 0.1

Ответ 15

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

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

Ответ 16

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

Вместо использованияhere strings"(<<<) с командой 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 для усечения до десятичных знаков, поэтому мы просто удалим эту часть. Здесь конечное решение:

    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

Связанный: