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

Med*_* Gh 211 floating-point bash arithmetic-expressions

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

RESULT=$(($IMG_WIDTH/$IMG2_WIDTH))
Run Code Online (Sandbox Code Playgroud)

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

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

echo "scale=2; ${userinput}" | bc 
Run Code Online (Sandbox Code Playgroud)

我怎么能让Bash给我一个漂浮0.5

Ign*_*ams 218

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

  • @Shevin`VAR = $(echo"scale = 2; $ IMG_WIDTH/$ IMG2_WIDTH"| bc)`或`VAR = $(bc <<<"scale = 2; $ IMG_WIDTH/$ IMG2_WIDTH")`没有$(( ))(双括号); 在执行命令之前由bash扩展 (48认同)
  • @NahuelFouilleul你在这里有最好的答案.应该真的是自己的答案,并接受为答案.这条特殊的线非常有用:VAR = $(bc <<<"scale = 2; $ IMG_WIDTH/$ IMG2_WIDTH") (8认同)
  • 如何委托像bc这样的工具将答案放在RESULT变量中? (5认同)
  • 没错,但是awk通常更有可能已经安装在系统中. (3认同)
  • 所以你的意思是像 VAR=$(echo "scale=2; $(($IMG_WIDTH/$IMG2_WIDTH))" | bc) ? (2认同)

aay*_*ubi 177

你可以这样做:

bc <<< 'scale=2; 100/3'
33.33
Run Code Online (Sandbox Code Playgroud)

更新 20130926:您可以使用:

bc -l <<< '100/3' # saves a few hits
Run Code Online (Sandbox Code Playgroud)

  • @AndreasSpindler有点旧的帖子,但是如果有人想知道,可以通过应用scale命令来改变它,例如.`bc -l <​​<<'scale = 2; 100/3'` (10认同)
  • ...并添加许多数字.至少在我的机器上这是yiels 33.33333333333333333333而前者给出了33.33. (7认同)
  • 谢谢@psmith。有趣的是,对于/,它说“结果的标度是可变标度的值”,但对于乘法则不是。我最好的例子是:`bc &lt;&lt;&lt;'scale = 1; 由于某种原因,1 * 3.00001'的比例实际上是5,`bc &lt;&lt;&lt;'scale = 1; 1 / 3.000001'的比例尺为1。有趣的是,将其除以1可直接设置为:'bc &lt;&lt;&lt;'scale = 1; 1 * 3.00001 / 1'`的比例是1 (2认同)
  • 为了防止[“语法错误:意外重定向”](/sf/ask/172362221/),请使用 `echo 'scale=2; 100/3' | 100/3' | bc` 而不是 `bc &lt;&lt;&lt; 'scale=2; 100/3'` (2认同)

Tho*_*hor 125

庆典

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

echo $(( 100 * 1 / 3 )) | sed 's/..$/.&/'
Run Code Online (Sandbox Code Playgroud)

输出:

.33
Run Code Online (Sandbox Code Playgroud)

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

备择方案

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

CLISP

clisp -x '(/ 1.0 3)'
Run Code Online (Sandbox Code Playgroud)

清理输出:

clisp --quiet -x '(/ 1.0 3)'
Run Code Online (Sandbox Code Playgroud)

或通过stdin:

echo '(/ 1.0 3)' | clisp --quiet | tail -n1
Run Code Online (Sandbox Code Playgroud)

DC

echo 2k 1 3 /p | dc
Run Code Online (Sandbox Code Playgroud)

天才cli计算器

echo 1/3.0 | genius
Run Code Online (Sandbox Code Playgroud)

gnuplot的

echo 'pr 1/3.' | gnuplot
Run Code Online (Sandbox Code Playgroud)

JQ

echo 1/3 | jq -nf /dev/stdin
Run Code Online (Sandbox Code Playgroud)

要么:

jq -n 1/3
Run Code Online (Sandbox Code Playgroud)

KSH

echo 'print $(( 1/3. ))' | ksh
Run Code Online (Sandbox Code Playgroud)

LUA

lua -e 'print(1/3)'
Run Code Online (Sandbox Code Playgroud)

或通过stdin:

echo 'print(1/3)' | lua
Run Code Online (Sandbox Code Playgroud)

最大值

echo '1/3,numer;' | maxima
Run Code Online (Sandbox Code Playgroud)

清理输出:

echo '1/3,numer;' | maxima --quiet | sed -En '2s/[^ ]+ [^ ]+ +//p'
Run Code Online (Sandbox Code Playgroud)

节点

echo 1/3 | node -p
Run Code Online (Sandbox Code Playgroud)

八度

echo 1/3 | octave
Run Code Online (Sandbox Code Playgroud)

perl的

echo print 1/3 | perl
Run Code Online (Sandbox Code Playgroud)

蟒蛇

echo print 1/3. | python2
Run Code Online (Sandbox Code Playgroud)

[R

echo 'print(1/3)' | python3
Run Code Online (Sandbox Code Playgroud)

清理输出:

echo 1/3 | R --no-save
Run Code Online (Sandbox Code Playgroud)

红宝石

echo 1/3 | R --vanilla --quiet | sed -n '2s/.* //p'
Run Code Online (Sandbox Code Playgroud)

wcalc

echo print 1/3.0 | ruby
Run Code Online (Sandbox Code Playgroud)

清理输出:

echo 1/3 | wcalc
Run Code Online (Sandbox Code Playgroud)

zsh的

echo 1/3 | wcalc | tr -d ' ' | cut -d= -f2
Run Code Online (Sandbox Code Playgroud)

其他来源

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

  • 很好的答案.我知道它是在问题发布几年后发布的,但更值得接受. (9认同)
  • 还应该强调的是 bc 不支持浮点数。它使用固定点数,这是一个很大的区别。 (3认同)
  • 如果您有可用的zsh,那么值得考虑使用zsh而不是Bash编写脚本 (2认同)

adr*_*lzt 39

改善马文的答案:

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

bc并不总是作为安装包.

  • awk脚本需要一个`exit`来阻止它从输入流中读取.我还建议使用awk的`-v`标志来防止倾斜牙签综合征.所以:`RESULT = $(awk -v dividend ="$ {IMG_WIDTH}" - v divisor ="$ {IMG2_WIDTH}"'BEGIN {printf"%.2f",dividend/divisor; exit(0)}') (4认同)
  • 一种更糟糕的方法是从输入流中读取参数:`RESULT = $(awk'{printf(“ result =%.2f \ n”,$ 1 / $ 2)}'&lt;&lt;&lt;“ $ IMG_WIDTH $ IMG2_WIDTH“`。 (2认同)

use*_*742 30

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

RESULT=$(echo "$IMG_WIDTH/$IMG2_WIDTH" | bc -l)
Run Code Online (Sandbox Code Playgroud)

  • 如果我的系统中没有包含`-l`,则bc不会进行浮点运算. (4认同)

mar*_*vin 28

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

例如:

echo "$IMG_WIDTH $IMG2_WIDTH" | awk '{printf "%.2f \n", $1/$2}'
Run Code Online (Sandbox Code Playgroud)

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

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

RESULT = `echo ...`
Run Code Online (Sandbox Code Playgroud)


Pen*_*eng 18

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

% IMG_WIDTH=1080
% IMG2_WIDTH=640
% result=$((IMG_WIDTH*1.0/IMG2_WIDTH))
% echo $result
1.6875
Run Code Online (Sandbox Code Playgroud)

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

  • 我是zsh的忠实粉丝并且在过去的4年里一直在使用它,但是交互式使用在这里是一个很好的重点.一个需要zsh的脚本通常不会在各种各样的机器上非常便携,因为它通常不是标准的,可悲的是(公平地说,也许没关系; OP没有说明它将如何使用). (3认同)

小智 14

好吧,在float之前是使用固定小数逻辑的时间:

IMG_WIDTH=100
IMG2_WIDTH=3
RESULT=$((${IMG_WIDTH}00/$IMG2_WIDTH))
echo "${RESULT:0:-2}.${RESULT: -2}"
33.33
Run Code Online (Sandbox Code Playgroud)

最后一行是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
Run Code Online (Sandbox Code Playgroud)

代码背后的基本原理是:在除以前乘以100得到2位小数.

  • 技术术语是[定点算术](https://en.wikipedia.org/wiki/Fixed-point_arithmetic) (2认同)

Gab*_*les 8

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

    这里不像最受欢迎的示例之一那样在命令中使用“ here strings ”( <<<) ,而是我最喜欢的浮点示例,就来自手册页的部分(请参阅参考资料 手册页)。bcbcEXAMPLESbcman bc

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

    1. 这是如何将浮点计算的结果存储到 bash 变量中——在本例中存储到名为 的变量中pi。请注意,scale=10在本例中将精度的小数位数设置为 10。此位之后的任何小数位都将被截断

      pi="$(echo "scale=10; 4*a(1)" | bc -l)"
      
      Run Code Online (Sandbox Code Playgroud)
    2. 现在,要使用一行代码来打印该变量的值,只需将该echo命令添加到末尾作为后续命令,如下所示。请注意小数点后 10 位的截断,如下所示:

      pi="$(echo "scale=10; 4*a(1)" | bc -l)"; echo "$pi"
      
      Run Code Online (Sandbox Code Playgroud)

      输出:

      3.1415926532
      
      Run Code Online (Sandbox Code Playgroud)
    3. 最后,让我们进行一些四舍五入。这里我们将使用该printf函数四舍五入到小数点后 4 位。请注意,3.14159...现在的轮次为3.1416。由于我们进行四舍五入,我们不再需要使用scale=10截断到小数点后 10 位,因此我们只需删除该部分即可。这是最终的解决方案:

      3.1415926532
      
      Run Code Online (Sandbox Code Playgroud)

      输出:

      3.1416
      
      Run Code Online (Sandbox Code Playgroud)
  2. 测量和打印运行时间:

    测量和打印程序的运行时间是上述技术的另一个非常好的应用和演示。

    (另请参阅我的其他答案)。

    方法如下。请注意,dt_min0.01666666666...to舍入0.017

    pi="$(printf "%.4f" $(echo "4*a(1)" | bc -l))"; echo "$pi"
    
    Run Code Online (Sandbox Code Playgroud)

    输出:

    dt_sec = 1; dt_min = 0.017
    
    Run Code Online (Sandbox Code Playgroud)

有关的:

  • 带有详细示例和用例的可靠答案以及使用 printf 的轮询 (2认同)

小智 6

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

\n
$IMG_WIDTH=1024\n$IMG2_WIDTH=2048\n\n$RATIO="$(( IMG_WIDTH / $IMG2_WIDTH )).$(( (IMG_WIDTH * 100 / IMG2_WIDTH) % 100 ))"\n
Run Code Online (Sandbox Code Playgroud)\n

这样,您会在纯 bash 中得到点后的 2 位数字,并被截断(称为四舍五入)(无需启动其他进程)。当然,如果您只需要该点后的一位数字,则乘以 10 并取模 10。

\n

这是做什么的:

\n
    \n
  • 首先$((...))进行整数除法;
  • \n
  • 第二个$((...))对 100 倍大的东西进行整数除法,本质上是将你的 2 位数字移到该点的左侧,然后 ( %) 通过取模得到这 2 位数字。
  • \n
\n

额外曲目bc\xc3\x97 1000 版本在我的笔记本电脑上花了 1.8 秒,而纯bash版本花了 0.016 秒。

\n


G. *_*III 5

虽然您不能在 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))
Run Code Online (Sandbox Code Playgroud)


beb*_*bbo 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;}
Run Code Online (Sandbox Code Playgroud)

或多行:

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
}
Run Code Online (Sandbox Code Playgroud)

现在你有了这个功能

div <dividend> <divisor> [<precision=2>]
Run Code Online (Sandbox Code Playgroud)

并像使用它一样

> div 1 2
.50

> div 273 123 5
2.21951

> x=$(div 22 7)
> echo $x
3.14
Run Code Online (Sandbox Code Playgroud)

更新 我添加了一个小脚本,它为您提供了 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
Run Code Online (Sandbox Code Playgroud)

这里的脚本:

#!/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 / ;}
Run Code Online (Sandbox Code Playgroud)


Wad*_* M. 5

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

如果你运行这个:

echo "scale=2; (100/180) * 180" | bc
Run Code Online (Sandbox Code Playgroud)

你得到的99.00只是,这会失去精度。

如果你这样运行:

echo "result = (100/180) * 180; scale=2; result / 1" | bc -l
Run Code Online (Sandbox Code Playgroud)

现在你得到了99.99

因为您仅在打印时进行缩放。

参考这里