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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8654051/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 21:25:06  来源:igfitidea点击:

How to compare two floating point numbers in Bash?

bashfloating-pointcomparisonnumbers

提问by Jonas

I am trying hard to compare two floating point numbers within a bash script. I have to variables, e.g.

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

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 的正确处理有一些问题,它可能是“电子格式”。:(

Any help, hints are welcome!

任何帮助,欢迎提示!

回答by Serge Stroobandt

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 bcreturns either 1 or 0.

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

The option -lis equivalent to --mathlib; it loads the standard math library.

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

Enclosing the whole expression between double parenthesis (( ))will translate these values to respectively true or false.

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

Please, ensure that the bcbasic calculator package is installed.

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

This equally works for floats in scientific format, provided a capital letter Eis employed, e.g. num1=3.44E6

如果使用大写字母,这同样适用于科学格式的浮点数E,例如num1=3.44E6

回答by alrusdi

bash handles only integer maths but you can use bccommand 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

注意指数符号必须大写

回答by anubhava

It's better to use awkfor non integer mathematics. You can use this bash utility function:

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

numCompare() {
   awk -v n1="" -v n2="" '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

回答by user

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

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

回答by ungalcrys

you can use awk combined with a bash if condition, awk will print 1or 0and those will be interpreted by if clause with trueor false.

您可以将 awk 与 bash if 条件结合使用,awk 将打印10,这些将被 if 子句解释为truefalse

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

回答by Elan Ruusam?e

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="" operator="" value="" 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

回答by tripleee

Of course, if you don't need really floating-point arithmetic, just arithmetic on e.g. 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.

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

回答by Thomas Kekeisen

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 $resultwill be 1in this case, otherwise 0.

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

The function:

功能:

is_first_floating_number_bigger () {
    number1=""
    number2=""

    [ ${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=""
    number2=""

    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 .shfile and include it like this:

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

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

回答by Ed Morton

I was posting this as an answer to https://stackoverflow.com/a/56415379/1745001when 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...

回答by prayagupd

This script may help where I'm checking if installed grailsversion is greater than minimum required. Hope it helps.

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