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


242

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

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

我确实研究过Bash指南,我知道我应该使用bc它们在互联网上使用的所有示例bc。在echo我试图把同样的事情在我的SCALE,但没有奏效。

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

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

我怎样才能让Bash给我一个漂浮的东西0.5


9
对尝试在脚本中执行浮点算术的每个人的评论,问自己:我真的需要浮点算术吗?有时候你真的可以没有。例如,请参阅BashFAQ / 022的最后一部分。
gniourf_gniourf 2015年

Answers:


242

你不能 bash 只做整数;您必须委托给诸如之类的工具bc


8
如何委托bc这样的工具来将答案放入RESULT变量中?
Medya Gh 2012年

2
所以你的意思是像VAR = $(echo“ scale = 2; $(($ IMG_WIDTH / $ IMG2_WIDTH))”“ | bc)吗?
Medya Gh 2012年

54
@Shevin VAR=$(echo "scale=2; $IMG_WIDTH/$IMG2_WIDTH" | bc)VAR=$(bc <<<"scale=2;$IMG_WIDTH/$IMG2_WIDTH")不带$(())(双括号);在执行命令之前由bash进行了扩展
Nahuel Fouilleul

4
是的,但是awk通常更可能已安装在系统中。
CMCDragonkai 2014年

9
@NahuelFouilleul您在这里有最好的答案。确实应该是自己的答案,并被接受为答案。这条特殊的行非常有用:VAR = $(bc <<<“ scale = 2; $ IMG_WIDTH / $ IMG2_WIDTH”)
大卫

197

你可以这样做:

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

更新 20130926:您可以使用:

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

8
...并增加许多数字。至少在我的机器上,此值为33.33333333333333333333,而前者为33.33。
Andreas Spindler 2014年

12
@AndreasSpindler有点旧的帖子,但是如果有人想知道,可以通过应用scale命令来更改。bc -l <<< 'scale=2; 100/3'
马蒂

请注意,如果您希望通过使用scale = 0获得一个整数供以后在bash中使用。从v1.06.95开始,由于输入数字中的小数部分,bc会忽略scale变量。也许这在文档中,但是我找不到。试试:echo $(bc -l <​​<<'scale = 0; 1 * 3.3333')
格雷格·贝尔

1
@GregBell手册页上说Unless specifically mentioned the scale of the result is the maximum scale of the expressions involved./操作员要注意一点:The scale of the result is the value of the variable scale.
psmith

2
谢谢@psmith。有趣的是,对于/,它说“结果的标度是可变标度的值”,但对于乘法则不是。我更好的例子:bc <<< 'scale=1; 1*3.00001' 由于某种原因,bc <<< 'scale=1; 1/3.000001' 比例尺实际上为5, 比例尺为1。有趣的是,除以1便将其直接设置为: bc <<< 'scale=1; 1*3.00001/1'比例尺为1
Greg Bell

136

重击

正如其他人所指出的bash那样,尽管您可以使用一些固定的十进制技巧(例如,使用两个小数)来伪造它,但它不支持浮点算术:

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

输出:

.33

有关类似但更简洁的方法,请参见Nilfred的答案

备择方案

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

剪辑

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

天才CLI计算器

echo 1/3.0 | genius

鬼脚本

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

gnuplot

echo 'pr 1/3.' | gnuplot

q

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

要么:

jq -n 1/3

sh

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

a

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

佩尔

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'

红宝石

echo print 1/3.0 | ruby

wcalc

echo 1/3 | wcalc

随着清理输出:

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

sh

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

单位

units 1/3

输出紧凑:

units --co 1/3

其他来源

StéphaneChazelas在Unix.SX上 回答了类似的问题


9
好答案。我知道它是在问题提出几年后发布的,但更值得被接受。
布赖恩·克莱恩

2
如果您有可用的zsh,那么值得考虑使用zsh而不是Bash编写脚本
Andrea Corbellini 18-4-29的

gnuplot大拇指:)
andywiecko

不错的清单。特别echo 1/3 | node -p是短。
Johnny Wong

39

改善marvin的答案:

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

bc并非总是作为已安装的软件包提供。


4
awk脚本需要一个,exit以防止其从其输入流中读取。我还建议使用awk的-v标志来防止倾斜的牙签综合征。所以:RESULT=$(awk -v dividend="${IMG_WIDTH}" -v divisor="${IMG2_WIDTH}" 'BEGIN {printf "%.2f", dividend/divisor; exit(0)}')
aecolley

2
一种更糟糕的方法是从输入流中读取参数: RESULT=$(awk '{printf("result= %.2f\n",$1/$2)}' <<<" $IMG_WIDTH $IMG2_WIDTH "
jmster

1
bc是POSIX的一部分,通常已预先安装。
fuz 2015年

这对我在Windows 7中使用git bash起作用...谢谢:)
The Beast 2016年

31

您可以在-l选项中使用bc (L字母)

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

4
如果-l我的系统中未包含,则bc不会进行浮点数学运算。
starbeamrainbowlabs 2015年

28

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

例如:

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

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

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

RESULT = `echo ...`

优秀的!谢谢。这对于不存在bc的嵌入式环境很有帮助。您为我节省了一些交叉编译时间。
狂热的迷

19

现在是尝试(几乎)bash超集zsh的绝佳时机,它具有许多其他不错的功能,包括浮点数学运算。这是您的示例在zsh中的样子:

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

这篇文章可能对您有帮助:bash-值得临时使用zsh吗?


3
我是zsh的忠实拥护者,并且在过去的4年中一直在使用它,但在这里重点强调了交互式使用。令人遗憾的是,需要zsh的脚本通常不会在各种机器上具有很好的可移植性,可悲的是(公平地说,也许还可以; OP并没有完全说明如何使用它)。
布赖恩·克莱恩

17

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


5

如果找到了首选项的变体,也可以将其包装到一个函数中。

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

UPDATE 我添加了一个小脚本,该脚本为您提供了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 / ;}

1
local _d=${3:-2}更简单
KamilCuk

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给出的圆的面积和直径


4

在某些情况下,您可能无法使用bc,因为它可能根本不存在,例如在某些缩减版的busybox或嵌入式系统中。在任何情况下,限制外部依赖性始终是一件好事,因此您始终可以将零除以(分子),这与乘以10的幂相同(您应根据以下公式选择10的幂)所需的精度),这将使除法输出整数。一旦拥有该整数,就将其视为字符串,并将小数点定位(从右向左移动)多次,等于乘以分子的十次幂。这是仅使用整数即可获得浮点结果的简单方法。


1
甚至Busybox都有Awk。也许这里应该有一个更突出的Awk答案。
Tripleee

4

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

4

我知道它很旧,但是很诱人。因此,答案是:您不能...但是您可以。让我们尝试一下:

$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版本x 1000用了1.8秒,而纯bash用了0.016秒。


3

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

这不是我最喜欢的示例之一,而是在命令中使用“ here strings ”(<<<),而是我最喜欢的浮点示例,它直接在手册页中(请参见bcbcEXAMPLESbcman bc手册页)。

在开始之前,请知道pi的方程是:pi = 4*atan(1)a()以下是的bc数学函数atan()

  1. 这是将浮点计算的结果存储到bash变量中的方法-在本例中为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函数将四舍五入到小数点后四位。请注意,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

有关:


1
可靠的答案以及详细的示例和使用案例以及使用printf进行取整
downvoteit


2

对于那些试图用可接受的答案来计算百分比但又失去精度的人:

如果运行此命令:

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

99.00只能得到,这正在失去精度。

如果以这种方式运行它:

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

现在你知道了99.99

因为您仅在打印时缩放。

请参考这里


1

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

注意:此答案的重点是为在bash(或其他shell)中执行数学运算的注入安全解决方案提供想法。当然,也可以使用它,只需稍作调整即可执行高级字符串处理等。

提出的大多数解决方案都是使用外部数据(变量,文件,命令行,环境变量)动态构建小型scriptlet。外部输入可用于将恶意代码注入引擎,其中许多

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

对于具有任意参数的python3: python3 -c 'import sys ; *a, = map(float, sys.argv[1:]) ; print(a[0] + a[1]*0.1 + a[2])' "$A" "$B"“ 4200.0” ==> 4205.63
WiringHarness,

0

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

echo "2.1+3.1" |  awk -F "+" '{print ($1+$2)}'
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.