我如何使用浮点运算在bash?

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

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

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

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

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

如何让Bash给我一个像0.5一样的浮点数?

437576 次浏览

你不能。bash 只有处理整数;你必须委托给一个工具,如bc

你可以这样做:

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

更新 20130926:可以使用:

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

现在是尝试zsh的最佳时机,它是一个(几乎)bash超集,具有许多其他不错的特性,包括浮点数学。下面是你的例子在zsh中的样子:

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

这篇文章可能会帮助你:bash -值得切换到zsh随意使用吗?

你可以通过-l选项使用bc (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 ...`

它不是真正的浮点数,但如果你想在一次调用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的圆的面积和直径

稍微改进一下马文的回答:

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

BC并不总是安装包。

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

bash

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

echo $(( 100 * 1 / 3 )) | sed -e 's/..$/.&/;t' -e 's/.$/.0&/'

输出:

.33

参见< em > Nilfred < / em >的答案获得类似但更简洁的方法。

选择

除了前面提到的bcawk选项之外,还有以下选项:

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

内容

echo 1 3 div = | gs -dNODISPLAY -dQUIET | sed -n '1s/.*>//p'

gnuplot

echo 'pr 1/3.' | gnuplot

Imagemagick

convert xc: -format '%[fx:1/3]' info:

或通过stdin:

echo 1/3 | { convert xc: -format "%[fx:$(cat)]" info:; }

金桥

jq -n 1/3

或通过stdin:

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

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'

节点

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

R

echo 1/3 | R --no-save

清理输出:

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

ruby

echo puts 1/3.0 | ruby

单位

units 1/3

输出紧凑:

units --com 1/3

wcalc

echo 1/3 | wcalc

清理输出:

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

zsh

print $(( 1/3. ))

或通过stdin:

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

其他来源

在UL上Stephane Chazelas 回答一个类似的问题

在浮点数出现之前,固定小数逻辑是被使用的:

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得到两个小数。

虽然在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))

如果你找到了你喜欢的变体,你也可以把它包装到一个函数中。

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

<强>更新 我添加了一个小脚本,为您提供了bash的基本浮点数操作:

用法:

> add 1.2 3.45
4.65
> sub 1000 .007
999.993
> mul 1.1 7.07
7.7770
> div 10 3
3.
> div 10 3.000
3.333

这里是脚本:

#!/bin/bash
__op() {
local z=00000000000000000000000000000000
local a1=${1%.*}
local x1=${1//./}
local n1=$((${#x1}-${#a1}))
local a2=${2%.*}
local x2=${2//./}
local n2=$((${#x2}-${#a2}))
local n=$n1
if (($n1 < $n2)); then
local n=$n2
x1=$x1${z:0:$(($n2-$n1))}
fi
if (($n1 > $n2)); then
x2=$x2${z:0:$(($n1-$n2))}
fi
if [ "$3" == "/" ]; then
x1=$x1${z:0:$n}
fi
local r=$(($x1"$3"$x2))
local l=$((${#r}-$n))
if [ "$3" == "*" ]; then
l=$(($l-$n))
fi
echo ${r:0:$l}.${r:$l}
}
add() { __op $1 $2 + ;}
sub() { __op $1 $2 - ;}
mul() { __op $1 $2 "*" ;}
div() { __op $1 $2 / ;}

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

echo "2.1+3.1" |  awk -F "+" '{print ($1+$2)}'
使用calc,这是我发现的最简单的方法 例子:< / p >

calc 1 + 1

 2

calc 1/10

 0.1

我知道很老了,但太诱人了。所以,答案是:你不能……但你可以。让我们试试这个:

$IMG_WIDTH=1024
$IMG2_WIDTH=2048


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

这样,在纯bash(不需要启动其他进程)中,在该点之后得到2位截断的数字(称为舍入到下位)。当然,如果你只需要这一点后面的一位数,你可以乘以10,然后对10取模。

它的作用:

  • first $((...))做整数除法;
  • 第二个$((...))对100倍大的数进行整数除法,本质上是将2位数字移到点的左边,然后(%)通过做模只得到这2位数字。

附送的歌曲: bc版本× 1000在我的笔记本电脑上花了1.8秒,而纯bash版本花了0.016秒。

如何在bash中进行浮点计算:

而不是使用"这里的字符串"(<<<)使用bc命令,就像most-upvoted例子中的一个那样,这是我最喜欢的bc浮点数示例,来自bc手册页的EXAMPLES部分(有关手册页,请参阅man bc)。

在我们开始之前,要知道π的方程是:pi = 4*atan(1)。下面的a()atan()bc数学函数。

  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_min0.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

相关:

** bash/shell中的注入安全浮点数学

注意:这个回答的重点是为在bash(或其他shell)中执行数学的注入安全解决方案提供思路。当然,同样也可以使用,只需要进行微小的调整来执行高级字符串处理等。

所提出的大多数解决方案都是使用外部数据(变量、文件、命令行、环境变量)动态构造小脚本。外部输入可用于向引擎中注入恶意代码,其中很多是恶意代码

下面是使用各种语言进行基本数学计算的比较,其中结果为浮点数。它计算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"

对于那些试图用公认的答案计算百分比,但失去精度的人:

如果你运行这个:

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

你只能得到99.00,这就失去了精度。

如果你这样运行:

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

现在你得到99.99

因为你只在打印的时候缩放。

参考在这里

正如其他人指出的那样,bash没有内置的浮点运算符。

你可以在bash中实现浮点,甚至不使用计算器程序,如公元前awk,或任何外部程序。

我在我的项目shellmath中正是这样做的,分三个基本步骤:

  1. 把数字分成整数部分和小数部分
  2. 使用内置的整数运算符分别处理各个部分,同时注意位值和进位
  3. 重新组合结果

作为一个挑逗,我添加了一个演示脚本,它使用以x=0为中心的泰勒级数计算e

如果您有时间,请查看一下。欢迎您的反馈!

您需要多精确的输出?如果你的用例已经可以接受通过bin的近似值,你甚至可以更进一步,利用POSIX退出码[0:256)(所有其他整数修改回该范围)。

在gawk/nawk/mawk-1中,它已经给了我epoch秒到整数级别,但我想扩展它以提取近毫秒的精度,但不是过分迂迂的,我在POSIX shell中运行这个命令

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

直接将表示gnu-date的0.xxxxxx输出的5位整数分配到256个bin中的1个,然后在awk获得system()调用的退出码(即所选的bin #)后撤销256。我发现这种方法比使用完整的getline调用开销更低。

该方法还直接将输出捕获到POSIX退出码中,而不是打印出一个额外的终端。

(如果这样写,shell算术auto floors it to integer而不是* 0.0256)。把它们放在awk函数中,就像这样。10#强制base-10以防止posix shell解释“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%的理发来考虑shell开销。

红利=除数×商+余数

我们只计算商和余数。 以及将这些字符串连接到一个变量中

新方法只对log_decimal除数有效:

function main() {
bar=10030
divisor=100
# divisor=50


quotient=$((bar / divisor))
# remainder=$((bar - v_int * divisor))
remainder=$((bar % divisor))
remainder_init=$remainder


printf "%-15s --> %s\n" "quotient" "$quotient"
printf "%-15s --> %s\n" "remainder" "$remainder"


cnt=0
while :; do
remainder=$((remainder * 10))
aux=$((remainder / divisor))
printf "%-15s --> %s\n" "aux" "$aux"
[[ aux -ne 0 ]] && break
((cnt += 1))
printf "%-15s --> %s\n" "remainder" "$remainder"
done
printf "%-15s --> %s\n" "cnt" "$cnt"
printf "%-15s --> %s\n" "aux" "$aux"


printf $quotient
printf "."
for i in $(seq 1 $cnt); do printf "0"; done
printf $remainder_init
}
clear
main

旧的错误方式:

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

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

让我们举个例子来理解,如果你想找到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"进行浮动计算