繁体   English   中英

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

[英]How to compare two floating point numbers in Bash?

我正在努力比较 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 的正确处理有一些问题,它可能是“电子格式”。

更方便

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

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

解释

通过基本计算器命令bc管道返回 1 或 0。

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

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

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

警告:指数符号应写为*10^ 不是E ,也不是e

例如:

$ echo '1*10^3==1000" |bc
1

然而

$ echo '1E3==1000" |bc
0

此处讨论了克服此bc限制的策略。

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

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

注意指数符号必须大写

最好将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

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

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

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

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

如果条件满足,您可以将awk与 bash 结合使用:

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

比较软件包版本的数字时要小心,例如检查 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

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

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

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

awk和类似的工具(我在盯着你sed ...)应该被归入旧项目的垃圾箱,因为它的代码是用永不阅读的语言编写的,所以每个人都不敢碰它。

或者您是需要优先考虑 CPU 使用优化而不是代码维护优化的相对罕见的项目……在这种情况下,继续。

如果没有,为什么不使用可读和明确的东西,比如python 你的程序员伙伴和未来的自己会感谢你。 您可以像所有其他人一样将python与 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

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

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

我使用了这里的答案并将它们放在一个函数中,你可以这样使用它:

is_first_floating_number_bigger 1.5 1.2
result="${__FUNCTION_RETURN}"

一旦调用,在这种情况下echo $result将为1 ,否则为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

请检查以下编辑过的代码:-

#!/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

这很好用。

当它作为此问题的副本关闭时,我将其发布为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...

这个脚本可以帮助我检查安装的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
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

使用 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

这个怎么样? =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

使用 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 混合提供了更多......

只需用 printf 替换echo (它理解浮点数):

st=$(  printf '%50G < %50G\n' "$num1" "$num2" | bc -l  )

有一种简单的方法,它比 AWK 快一点,并且不需要安装bc 它利用sort对浮点数进行排序的能力:

A=1280.4
B=9.325
LOW=$(sort -n <<< "$A"$'\n'"$B" | head -1)
if [[ "$LOW" == "$A" ]]; then
    echo "A <= B"
else
    echo "A >= B"
fi

当然,它不适用于相等的数字

一个班轮解决方案

假设你有两个变量AB

echo "($A > $B) * $B + ($A < $B) * $A" | bc

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM