简体   繁体   English

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

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

I am trying hard to compare two floating point numbers within a bash script.我正在努力比较 bash 脚本中的两个浮点数。 I have to variables, eg我必须变量,例如

let num1=3.17648e-22
let num2=1.5

Now, I just want do a simple comparison of these two numbers:现在,我只想对这两个数字做一个简单的比较:

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

Unfortunately, I have some problems with the right treatment of the num1 which can be of the "e-format".不幸的是,我对 num1 的正确处理有一些问题,它可能是“电子格式”。

More conveniently更方便

This can be done more conveniently using Bash's numeric context:这可以使用 Bash 的数字上下文更方便地完成:

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

Explanation解释

Piping through the basic calculator command bc returns either 1 or 0.通过基本计算器命令bc管道返回 1 或 0。

The option -l is equivalent to --mathlib ;选项-l等效于--mathlib it loads the standard math library.它加载标准数学库。

Enclosing the whole expression between double parenthesis (( )) will translate these values to respectively true or false.将整个表达式括在双括号(( ))会将这些值分别转换为 true 或 false。

Please, ensure that the bc basic calculator package is installed.请确保安装了bc基本计算器包。

Caveat: Exponential notation should be written as *10^ ;警告:指数符号应写为*10^ not E , nor e .不是E ,也不是e

For example:例如:

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

Whereas然而

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

Strategies to overcome this bc limitation are discussed here .此处讨论了克服此bc限制的策略。

bash handles only integer maths but you can use bc command as follows: bash 仅处理整数数学,但您可以使用bc命令,如下所示:

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

Note that exponent sign must be uppercase注意指数符号必须大写

It's better to use awk for non integer mathematics.最好将awk用于非整数数学。 You can use this bash utility function:您可以使用此 bash 实用程序功能:

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

And call it as:并将其称为:

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

Pure bash solution for comparing floats without exponential notation, leading or trailing zeros:用于比较没有指数符号、前导或尾随零的浮点数的纯 bash 解决方案:

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

Order of logical operators matters .逻辑运算符的顺序很重要 Integer parts are compared as numbers and fractional parts are intentionally compared as strings.整数部分作为数字进行比较,小数部分有意作为字符串进行比较。 Variables are split into integer and fractional parts using this method .使用此方法将变量拆分为整数部分和小数部分。

Won't compare floats with integers (without dot).不会将浮点数与整数(不带点)进行比较。

You can use awk combined with a bash if condition:如果条件满足,您可以将awk与 bash 结合使用:

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

beware when comparing numbers that are package versions, like checking if grep 2.20 is greater than version 2.6:比较软件包版本的数字时要小心,例如检查 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

I solved such problem with such shell/awk function:我用这样的 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

Of course, if you don't need really floating-point arithmetic, just arithmetic on eg dollar values where there are always exactly two decimal digits, you might just drop the dot (effectively multiplying by 100) and compare the resulting integers.当然,如果您不需要真正的浮点算术,只需对美元值进行算术运算,其中始终只有两位十进制数字,您可能只需删除点(有效乘以 100)并比较结果整数。

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

This obviously requires you to be sure that both values have the same number of decimal places.这显然要求您确保两个值具有相同的小数位数。

awk and tools like it (I'm staring at you sed ...) should be relegated to the dustbin of old projects, with code that everyone is too afraid to touch since it was written in a read-never language. awk和类似的工具(我在盯着你sed ...)应该被归入旧项目的垃圾箱,因为它的代码是用永不阅读的语言编写的,所以每个人都不敢碰它。

Or you're the relatively rare project that needs to prioritize CPU usage optimization over code maintenance optimization... in which case, carry on.或者您是需要优先考虑 CPU 使用优化而不是代码维护优化的相对罕见的项目……在这种情况下,继续。

If not, though, why not instead just use something readable and explicit, such as python ?如果没有,为什么不使用可读和明确的东西,比如python Your fellow coders and future self will thank you.你的程序员伙伴和未来的自己会感谢你。 You can use python inline with bash just like all the others.您可以像所有其他人一样将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

A solution supporting all possible notations, including the scientific notation with both uppercase and lowercase exponents (eg, 12.00e4 ):支持所有可能的符号的解决方案,包括具有大写和小写指数的科学记数法(例如, 12.00e4 ):

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

I used the answers from here and put them in a function, you can use it like this:我使用了这里的答案并将它们放在一个函数中,你可以这样使用它:

is_first_floating_number_bigger 1.5 1.2
result="${__FUNCTION_RETURN}"

Once called, echo $result will be 1 in this case, otherwise 0 .一旦调用,在这种情况下echo $result将为1 ,否则为0

The function:功能:

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}"
}

Or a version with debug output:或带有调试输出的版本:

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}"
}

Just save the function in a separated .sh file and include it like this:只需将函数保存在一个单独的.sh文件中,并像这样包含它:

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

please check the below edited code:-请检查以下编辑过的代码:-

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

this works well.这很好用。

I was posting this as an answer to https://stackoverflow.com/a/56415379/1745001 when it got closed as a dup of this question so here it is as it applies here too:当它作为此问题的副本关闭时,我将其发布为https://stackoverflow.com/a/56415379/1745001的答案,因此这里也适用:

For simplicity and clarity just use awk for the calculations as it's a standard UNIX tool and so just as likely to be present as bc and much easier to work with syntactically.为简单起见,只需使用 awk 进行计算,因为它是标准的 UNIX 工具,因此与 bc 一样可能出现,并且在语法上更易于使用。

For this question:对于这个问题:

$ 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

and for that other question that was closed as a dup of this one:对于另一个问题,作为这个问题的重复而结束:

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

This script may help where I'm checking if installed grails version is greater than minimum required.这个脚本可以帮助我检查安装的grails版本是否大于最低要求。 Hope it helps.希望能帮助到你。

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

Use korn shell, in bash you may have to compare the decimal part separately使用 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

How about this?这个怎么样? =D =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

Using bashj ( https://sourceforge.net/projects/bashj/ ), a bash mutant with java support, you just write (and it IS easy to read):使用 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

Of course bashj bash/java hybridation offers much more...当然 bashj bash/java 混合提供了更多......

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

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

There's one simple approach which is a bit faster than AWK and does not require bc to be installed.有一种简单的方法,它比 AWK 快一点,并且不需要安装bc It leverages sort 's ability to sort float numbers:它利用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

Of course, it does not work for numbers that are equal .当然,它不适用于相等的数字

One liner solution一个班轮解决方案

Suppose you have two variables A and B假设你有两个变量AB

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

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

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