Как использовать деление с плавающей запятой в bash?
Я пытаюсь разделить две ширины изображения в сценарии Bash, но bash дает мне 0
в качестве результата:
RESULT=$(($IMG_WIDTH/$IMG2_WIDTH))
Я изучил руководство Bash, и я знаю, что должен использовать bc
, во всех примерах в Интернете они используют bc
. В echo
я попытался поместить то же самое в свою SCALE
но это не сработало.
Вот пример, который я нашел в учебниках:
- Сравнение поплавковых и двойных типов данных в объекте C
- Float и двойной тип данных в Java
- проблема с плавающей запятой в R?
- Как анализировать float с двумя десятичными знаками в javascript?
- C ++ десятичные типы данных
echo "scale=2; ${userinput}" | bc
Как я могу заставить Баша дать мне поплавок, равный 0.5
?
- Преобразование float в double без потери точности
- Операции с плавающей запятой в C-ассоциативном?
- "F" после числа / float в Objective-C / C
- Могут ли какие-либо процессоры реального мира не использовать IEEE 754?
- Java: зачем вам нужно указывать 'f' в streamовом литерале?
- Как получить знак, мантисса и показатель числа с плавающей запятой
- C: Как поместить float на интервал [-pi, pi)
- Когда следует использовать double вместо десятичного?
Вы не можете. bash только целые числа; вы должны делегировать инструмент, такой как bc
.
вы можете сделать это:
bc <<< 'scale=2; 100/3' 33.33
UPDATE 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)'
или через stdin:
echo '(/ 1.0 3)' | clisp --quiet | tail -n1
Округ Колумбия
echo 2k 1 3 /p | dc
гениальный калькулятор cli
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)'
или через stdin:
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 возвращать число с плавающей запятой с двумя цифрами после десятичной точки. Я использовал эхо-канал для переменных в качестве полей, так как awk корректно работает с ними. «$ 1» и «$ 2» относятся к первому и второму полям, введенным в awk.
И вы можете сохранить результат как какую-то другую переменную, используя:
RESULT = `echo ...`
Это идеальное время, чтобы попробовать zsh, (почти) bash superset, со многими дополнительными приятными функциями, включая математику с плавающей запятой. Вот как ваш пример будет выглядеть в zsh:
% IMG_WIDTH=1080 % IMG2_WIDTH=640 % result=$((IMG_WIDTH*1.0/IMG2_WIDTH)) % echo $result 1.6875
Это сообщение может вам помочь: bash – стоит переключиться на zsh для случайного использования?
Хорошо, прежде чем плавать было время, когда была использована логика с фиксированными десятичными знаками:
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 десятичных знака.
Существуют сценарии, в которых вы не можете использовать bc, потому что это может просто не присутствовать, например, в некоторых сокращенных версиях busybox или встроенных систем. В любом случае ограничение внешних зависимостей всегда полезно, так что вы всегда можете добавить нули к числу, делящемуся на (числитель), то есть то же самое, что умножить на мощность 10 (вы должны выбрать мощность 10 согласно нужная вам точность), которая заставит деление выводить целое число. Когда у вас есть это целое число, рассматривайте его как строку и поместите десятичную точку (перемещая ее справа налево), количество раз равное мощности десяти, умноженное на числитель. Это простой способ получения результатов float с использованием только целочисленных чисел.
Это не плавающая точка, но если вы хотите что-то, что задает более одного результата в одном вызове 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
Хотя вы не можете использовать деление с плавающей запятой в 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))