如何在Bash中比较两个浮点数?


156

我正在努力比较bash脚本中的两个浮点数。我必须要变量,例如

let num1=3.17648e-22
let num2=1.5

现在,我只想对这两个数字进行简单比较:

st=`echo "$num1 < $num2" | bc`
if [ $st -eq 1]; then
  echo -e "$num1 < $num2"
else
  echo -e "$num1 >= $num2"
fi

不幸的是,我对num1的正确处理存在一些问题,它可以是“电子格式”。:(

任何帮助,欢迎提示!


2
“电子格式”是指指数符号(也称为科学符号)
乔纳斯(Jonas

Answers:


181

更方便

使用Bash的数字上下文可以更方便地完成此操作:

if (( $(echo "$num1 > $num2" |bc -l) )); then
  
fi

说明

通过基本计算器命令进行配管将bc返回1或0。

该选项-l等效于--mathlib; 它会加载标准数学库。

将整个表达式括在双括号之间(( ))会将这些值分别转换为true或false。

请确保bc已安装基本计算器包。

如果使用大写字母E,例如,这对于科学格式的花车同样有效num1=3.44E6


1
stackoverflow.com/questions/8654051/…相同的问题,例如$ echo“ 1.1 + 2e + 02” | bc(standard_in)1:语法错误
Nemo

1
@MohitArora请确保您已bc安装计算器包。
Serge Stroobandt

1
我对这0: not found句话很满意if (( $(echo "$TOP_PROCESS_PERCENTAGE > $THRESHOLD" | bc -l) )); then
Stephane

1
对于所有获得“未找到命令”的用户,请记住您需要将bc反引号或反斜线括起来$(),然后再封装到(( ))...即(( $(bc -l<<<"$a>$b") ))and not中(( bc -l<<<"$a>$b" ))
Normadize

@Nemo用科学记数法用大写字母写数字E,所有语法错误都将消失。
Serge Stroobandt

100

bash仅处理整数数学,但是您可以使用bc如下命令:

$ num1=3.17648E-22
$ num2=1.5
$ echo $num1'>'$num2 | bc -l
0
$ echo $num2'>'$num1 | bc -l
1

请注意,指数符号必须为大写


3
是的,但是要解决错误的计算方法,需要使用科学数字符号大写的“ e”符号,并使用-l标志对bc编程以进行预定义的数学例程
alrusdi 2011年

2
然后,您应该在答案中指出这一点,而不是仅仅发布一个非常相似的解决方案,而不要提及重要的差异。
Daniel Persson

4
这不是一个非常相似的解决方案。Alrusdi的解决方案使用该bc工具,这就是我向任何BASH程序员推荐的工具。BASH是无类型语言。是的,它可以执行整数运算,但是对于浮点数,您必须使用一些外部工具。BC是最好的,因为这就是它的目的。
DejanLekic 2011年

8
因为他试图在if语句中使用它,所以我会证明这一点。如果[$(... | bc -l)== 1]; 然后...
罗伯特·雅各布斯

27

最好awk用于非整数数学。您可以使用以下bash实用程序功能:

numCompare() {
   awk -v n1="$1" -v n2="$2" 'BEGIN {printf "%s " (n1<n2?"<":">=") " %s\n", n1, n2}'
}

并将其称为:

numCompare 5.65 3.14e-22
5.65 >= 3.14e-22

numCompare 5.65e-23 3.14e-22
5.65e-23 < 3.14e-22

numCompare 3.145678 3.145679
3.145678 < 3.145679

2
我喜欢这个答案,人们倾向于回避awk esp初学者,他们似乎认为比实际困难得多,我认为人们被花括号和看似语言混合的语法所吓倒(一眼)。而且由于几乎可以保证awk也将出现在目标系统上,就像bc一样(不确定是否安装了哪一个)。我喜欢bash脚本,但是没有浮点数,甚至没有微不足道的2个小数位(我猜有人可以为此写一个“假”包装器),确实很烦人……
osirisgothra

2
自远古以来,在外壳程序脚本中使用awkbc脚本是一种标准做法,我想说一些功能从未添加到外壳程序中,因为它们可以在awk,bc和其他Unix工具中使用。Shell脚本中无需纯洁。
piokuc 2014年

1
@WanderingMind这样做的一种方法是将0或1传递给exitAwk ,以便Awk以正确的,机器可读的方式将结果传递回Shell。 if awk -v n1="123.456" -v n2="3.14159e17" 'BEGIN { exit (n1 <= n2) }' /dev/null; then echo bigger; else echo not; fi...尽管请注意条件是如何反转的(退出状态0表示Shell成功)。
tripleee

1
为什么呢python。你已经perl在许多Linux / Unix系统默认安装..甚至php
anubhava

1
awk就我而言,此解决方案比bc由于我未得到的原因而返回错误结果的解决方案更可靠。
MBR

22

纯bash解决方案,用于比较没有指数符号,前导或尾随零的浮点数:

if [ ${FOO%.*} -eq ${BAR%.*} ] && [ ${FOO#*.} \> ${BAR#*.} ] || [ ${FOO%.*} -gt ${BAR%.*} ]; then
  echo "${FOO} > ${BAR}";
else
  echo "${FOO} <= ${BAR}";
fi

逻辑运算符的顺序很重要。将整数部分作为数字进行比较,并将小数部分作为字符串进行有意比较。使用此方法将变量分为整数部分和小数部分。

不会将浮点数与整数(不带点)进行比较。


15

您可以将awk与bash if条件结合使用,awk将输出10,并且将由if子句使用truefalse进行解释。

if awk 'BEGIN {print ('$d1' >= '$d2')}'; then
    echo "yes"
else 
    echo "no"
fi

使用awk很棒,因为它可以处理浮点数,但我个人更喜欢Synthaxif (( $(echo $d1 $d2 | awk '{if ($1 > $2) print 1;}') )); then echo "yes"; else echo "no"; fi
David Georg Reichelt

7

比较软件包版本号时要当心,例如检查grep 2.20是否大于2.6版本:

$ awk 'BEGIN { print (2.20 >= 2.6) ? "YES" : "NO" }'
NO

$ awk 'BEGIN { print (2.2 >= 2.6) ? "YES" : "NO" }'
NO

$ awk 'BEGIN { print (2.60 == 2.6) ? "YES" : "NO" }'
YES

我用这样的shell / awk函数解决了这样的问题:

# get version of GNU tool
toolversion() {
    local prog="$1" operator="$2" value="$3" version

    version=$($prog --version | awk '{print $NF; exit}')

    awk -vv1="$version" -vv2="$value" 'BEGIN {
        split(v1, a, /\./); split(v2, b, /\./);
        if (a[1] == b[1]) {
            exit (a[2] '$operator' b[2]) ? 0 : 1
        }
        else {
            exit (a[1] '$operator' b[1]) ? 0 : 1
        }
    }'
}

if toolversion grep '>=' 2.6; then
   # do something awesome
fi

在基于Debian的系统上,dpkg --compare-versions通常很有用。它具有比较内置的Debian软件包版本的完整逻辑,而不仅仅是Debian版本x.y
尼尔·梅休

5

当然,如果您不需要真正的浮点算术,只需对始终有两个十进制数字的美元值进行算术,则可以只删除点(有效乘以100)并比较所得整数。

if [[ $((10#${num1/.})) < $((10#${num2/.})) ]]; then
    ...

显然,这要求您确保两个值的小数位数相同。


3

我从这里使用了答案并将其放入函数中,可以像这样使用它:

is_first_floating_number_bigger 1.5 1.2
result="${__FUNCTION_RETURN}"

一旦调用,echo $result1在这种情况下,否则0

功能:

is_first_floating_number_bigger () {
    number1="$1"
    number2="$2"

    [ ${number1%.*} -eq ${number2%.*} ] && [ ${number1#*.} \> ${number2#*.} ] || [ ${number1%.*} -gt ${number2%.*} ];
    result=$?
    if [ "$result" -eq 0 ]; then result=1; else result=0; fi

    __FUNCTION_RETURN="${result}"
}

或带有调试输出的版本:

is_first_floating_number_bigger () {
    number1="$1"
    number2="$2"

    echo "... is_first_floating_number_bigger: comparing ${number1} with ${number2} (to check if the first one is bigger)"

    [ ${number1%.*} -eq ${number2%.*} ] && [ ${number1#*.} \> ${number2#*.} ] || [ ${number1%.*} -gt ${number2%.*} ];
    result=$?
    if [ "$result" -eq 0 ]; then result=1; else result=0; fi

    echo "... is_first_floating_number_bigger: result is: ${result}"

    if [ "$result" -eq 0 ]; then
        echo "... is_first_floating_number_bigger: ${number1} is not bigger than ${number2}"
    else
        echo "... is_first_floating_number_bigger: ${number1} is bigger than ${number2}"
    fi

    __FUNCTION_RETURN="${result}"
}

只需将函数保存在单独的.sh文件中,然后像这样包含它:

. /path/to/the/new-file.sh

3

我将其发布为https://stackoverflow.com/a/56415379/1745001的答案,因为它作为该问题的副本而被关闭,因此在这里也是如此:

为了简单明了,只需将awk用于计算即可,因为它是标准的UNIX工具,因此与bc一样存在,并且在语法上更容易使用。

对于这个问题:

$ cat tst.sh
#!/bin/bash

num1=3.17648e-22
num2=1.5

awk -v num1="$num1" -v num2="$num2" '
BEGIN {
    print "num1", (num1 < num2 ? "<" : ">="), "num2"
}
'

$ ./tst.sh
num1 < num2

对于作为该问题的重复项而关闭的另一个问题:

$ cat tst.sh
#!/bin/bash

read -p "Operator: " operator
read -p "First number: " ch1
read -p "Second number: " ch2

awk -v ch1="$ch1" -v ch2="$ch2" -v op="$operator" '
BEGIN {
    if ( ( op == "/" ) && ( ch2 == 0 ) ) {
        print "Nope..."
    }
    else {
        print ch1 '"$operator"' ch2
    }
}
'

$ ./tst.sh
Operator: /
First number: 4.5
Second number: 2
2.25

$ ./tst.sh
Operator: /
First number: 4.5
Second number: 0
Nope...

@DudiBoy不,它是清晰,简单,可移植的awk代码,或者是非显而易见的,晦涩的,依赖于shell的shell + bc代码。
Ed Morton

2

在检查安装grails版本是否大于最低要求时,此脚本可能会有所帮助。希望能帮助到你。

#!/bin/bash                                                                                         

min=1.4                                                                                             
current=`echo $(grails --version | head -n 2 | awk '{print $NF}' | cut -c 1-3)`                         

if [ 1 -eq `echo "${current} < ${min}" | bc` ]                                                          
then                                                                                                
    echo "yo, you have older version of grails."                                                   
else                                                                                                                                                                                                                       
    echo "Hurray, you have the latest version" 
fi

2
num1=0.555
num2=2.555


if [ `echo "$num1>$num2"|bc` -eq 1 ]; then
       echo "$num1 is greater then $num2"
else
       echo "$num2 is greater then $num1"
fi

2

请检查以下修改后的代码:-

#!/bin/bash

export num1=(3.17648*e-22)
export num2=1.5

st=$((`echo "$num1 < $num2"| bc`))
if [ $st -eq 1 ]
  then
    echo -e "$num1 < $num2"
  else
    echo -e "$num1 >= $num2"
fi

这很好。


2

awk诸如此类的工具(我正在凝视着您sed……)应该被降级到旧项目的垃圾箱中,因为每个代码都是用从未读过的语言编写的,因此每个人都不敢触摸。

或者您是一个相对罕见的项目,需要将CPU使用率优化优先于代码维护优化……在这种情况下,请继续。

如果不是,为什么不改而使用可读性和显性性的东西,例如python?您的编码员和未来的自己会感谢您。您可以python像其他所有bash一样使用内联bash。

num1=3.17648E-22
num2=1.5
if python -c "exit(0 if $num1 < $num2 else 1)"; then
    echo "yes, $num1 < $num2"
else
    echo "no, $num1 >= $num2"
fi

@Witiko我的原始版本有点奇怪。
CivFan

更加简洁:使用not(...)而不是0 if ... else 1
Neil Mayhew

1
如果您将awk和sed(我在看您的CivFan)降级为历史的垃圾箱,则您是糟糕的系统管理员,并且您输入的代码太多。(而且我喜欢并使用Python,因此并非如此)。-1用于放错位置的贪睡。这些工具在系统领域中占有一席之地,是Python还是没有。
Mike S

1
有趣的是,我最终获得了出色的ol'Perl!awk '${print $5}' ptpd_log_file | perl -ne '$_ > 0.000100 && print' > /tmp/outfile。十分简单。每种语言都有其位置。
Mike S

1
不要让seds语法古怪。与python不同,awk是每个UNIX安装上的强制性实用程序,而awk的等效项python -c "import sys; sys.exit(0 if float($num1) < float($num2) else 1)"就是simple awk "BEGIN{exit ($num1 > $num2 ? 0 : 1)}"
Ed Morton

2

一个支持所有可能表示法的解决方案,包括具有大写和小写指数(例如12.00e4)的科学表示法:

if (( $(bc -l <<< "${value1/e/E} < ${value2/e/E}") ))
then
    echo "$value1 is below $value2"
fi 

1

使用korn shell,在bash中可能必须分别比较小数部分

#!/bin/ksh
X=0.2
Y=0.2
echo $X
echo $Y

if [[ $X -lt $Y ]]
then
     echo "X is less than Y"
elif [[ $X -gt $Y ]]
then
     echo "X is greater than Y"
elif [[ $X -eq $Y ]]
then
     echo "X is equal to Y"
fi

2
问题是许多发行版都没有安装ksh,并且如果您的脚本将被其他人使用,则它们倾向于不喜欢安装额外的内容,尤其是当它只是应该以bash编写的脚本时-会认为他们不需要另一个shell来执行此操作,这首先破坏了使用bash脚本的全部原因-确保我们也可以在C ++中对其进行编码,但是为什么呢?
osirisgothra

没有安装ksh的发行版有哪些?
piokuc 2014年

1
@piokuc,例如Ubuntu Desktop&Server。我会说这是非常重要的一件事
Olli 2014年

另外,问题专门要求在bash中有效的解决方案。可能确实有很好的理由。说,这是大型应用程序的一部分,将所有内容迁移到ksh是不可行的。或者它正在嵌入式平台上运行,而在该平台上确实要安装另一个外壳。
Olli 2014年

1

使用bashj(https://sourceforge.net/projects/bashj/)(具有Java支持的bash突变体),您只需编写(而且很容易阅读):

#!/usr/bin/bashj

#!java
static int doubleCompare(double a,double b) {return((a>b) ? 1 : (a<b) ? -1 : 0);}

#!bashj
num1=3.17648e-22
num2=1.5
comp=j.doubleCompare($num1,$num2)
if [ $comp == 0 ] ; then echo "Equal" ; fi
if [ $comp == 1 ] ; then echo "$num1 > $num2" ; fi
if [ $comp == -1 ] ; then echo "$num2 > $num1" ; fi

当然bashj bash / java杂交提供了更多...


0

这个怎么样?= D

VAL_TO_CHECK="1.00001"
if [ $(awk '{printf($1 >= $2) ? 1 : 0}' <<<" $VAL_TO_CHECK 1 ") -eq 1 ] ; then
    echo "$VAL_TO_CHECK >= 1"
else
    echo "$VAL_TO_CHECK < 1"
fi

1
Awk脚本应仅exit 0报告真实情况并exit 1返回false;那么您可以简化为非常优雅的if awk 'BEGIN { exit (ARGV[1] >= ARGV[2]) ? 0 : 1 }' "$VAL_TO_CHECK" 1; then...(如果将Awk脚本封装在shell函数中,则更加优雅)。
三胞胎
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.