Как использовать деление с плавающей точкой в 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()
.
Вот как сохранить результат вычисления с плавающей запятой в переменной bash - в данном случае в переменной с именем
pi
. Обратите внимание, чтоscale=10
в этом случае устанавливает количество десятичных цифр точности равным 10. Все десятичные цифры после этого места обрезаются.pi=$(echo "scale=10; 4*a(1)" | bc -l)
Теперь, чтобы иметь одну строку кода, которая также выводит значение этой переменной, просто добавьте
echo
до конца в качестве последующей команды, как показано ниже. Обратите внимание на усечение до 10 знаков после запятой, как указано:pi=$(echo "scale=10; 4*a(1)" | bc -l); echo $pi 3.1415926532
Наконец, давайте немного округлим. Здесь мы будем использовать
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
Связанные с:
- https://unix.stackexchange.com/questions/52313/how-to-get-execution-time-of-a-script-effectively/547849
- Что означают три левые угловые скобки (`<<<`) в bash?
- https://unix.stackexchange.com/questions/80362/what-does-mean/80368
- https://askubuntu.com/questions/179898/how-to-round-decimals-using-bc-in-bash/574474
Если вы нашли вариант вашего предпочтения, вы также можете включить его в функцию.
Здесь я оборачиваю некоторый 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-значного целого числа, представляющего
Этот метод также напрямую записывает вывод в код выхода POSIX вместо дополнительной распечатки терминала.
(арифметика оболочки
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 " для вычислений с плавающей запятой.