如何在bash中使用浮点除法?

问题描述 投票:187回答:13

我试图在Bash脚本中划分两个图像宽度,但是bash给了我0作为结果:

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

我确实研究了Bash指南,我知道我应该使用bc,在互联网的所有例子中他们都使用bc。在echo我试图把同样的东西放在我的SCALE但它没有用。

以下是我在教程中找到的示例:

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

我怎样才能让Bash给我一个像0.5一样的浮动?

bash floating-point arithmetic-expressions
13个回答
206
投票

你不能。 bash只做整数;你必须委托给bc这样的工具。


4
投票

有些场景你不能使用bc因为它可能根本就不存在,比如在busybox或嵌入式系统的某些减少版本中。在任何情况下,限制外部依赖性始终是一件好事,所以你总是可以将零加到除以(分子)的数字,这与乘以10的幂相同(你应该选择10的幂)你需要的精度),这将使除法输出一个整数。一旦你有了这个整数就把它当作一个字符串并将小数点(从右向左移动)的次数等于10乘以分子的幂。这是通过仅使用整数来获得浮点结果的简单方法。


3
投票

如果找到了您偏好的变体,您也可以将其包装到函数中。

在这里,我将一些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

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))

1
投票

使用calc。这是我发现的最简单的例子:

计算1 + 1

 2

计算1/10

 0.1

0
投票

这是awk命令:-F =字段分隔符== +

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

161
投票

你可以这样做:

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

更新20130926:您可以使用:

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

112
投票

bash

正如其他人所指出的那样,bash不支持浮点运算,尽管你可以用一些固定的十进制技巧伪造它,例如有两位小数:

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

输出:

.33

请参阅Nilfred's answer以获得类似但更简洁的方法。

Alternatives

除了提到的bcawk替代品,还有以下内容:

clisp

clisp -x '(/ 1.0 3)'

清理输出:

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

或通过stdin:

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

dc

echo 2k 1 3 /p | dc

genius cli calculator

echo 1/3.0 | genius

gnuplot

echo 'pr 1/3.' | gnuplot

jq

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

要么:

jq -n 1/3

ksh

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

lua

lua -e 'print(1/3)'

或通过stdin:

echo 'print(1/3)' | lua

maxima

echo '1/3,numer;' | maxima

清理输出:

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

node

echo 1/3 | node -p

octave

echo 1/3 | octave

perl

echo print 1/3 | perl

python2

echo print 1/3. | python2

python3

echo 'print(1/3)' | python3

R

echo 1/3 | R --no-save

清理输出:

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

ruby

echo print 1/3.0 | ruby

wcalc

echo 1/3 | wcalc

清理输出:

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

zsh

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

units

units 1/3

紧凑的输出:

units --co 1/3

Other sources

在Unix.SX上Stéphane Chazelas answered a similar question结束。


36
投票

改善马文的答案:

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

bc并不总是作为安装包。


27
投票

您可以通过-l选项(L字母)使用bc

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

26
投票

作为bc的替代方案,您可以在脚本中使用awk。

例如:

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

在上面,“%。2f”告诉printf函数返回小数位后两位数的浮点数。由于awk在它们上正常运行,我使用echo将变量作为字段输入管道。 “$ 1”和“$ 2”指的是输入awk的第一个和第二个字段。

您可以使用以下结果将结果存储为其他变量:

RESULT = `echo ...`

17
投票

现在是尝试zsh(一个(几乎)bash超集)的最佳时机,还有许多其他不错的功能,包括浮点数学。以下是您在zsh中的示例:

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

这篇文章可以帮到你:bash - Worth switching to zsh for casual use?


13
投票

好吧,在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位小数。


4
投票

它不是真正的浮点数,但是如果你想在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美元给出的圆的面积和直径

© www.soinside.com 2019 - 2024. All rights reserved.