Как сравнить два числа с плавающей точкой в ​​Bash? - PullRequest
113 голосов
/ 28 декабря 2011

Я очень стараюсь сравнить два числа с плавающей точкой в ​​скрипте 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, которая может быть "электронного формата". (

Любая помощь, советы приветствуются!

Ответы [ 17 ]

119 голосов
/ 27 июня 2015

удобнее

Это можно сделать удобнее, используя числовой контекст Bash:

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

Объяснение

Передача через базовую команду калькулятора bc возвращает либо 1, либо 0.

Опция -l эквивалентна --mathlib; загружает стандартную математическую библиотеку.

Заключив все выражение в двойные скобки (( )) переведет эти значения соответственно в true или false.

Пожалуйста, убедитесь, что установлен базовый калькулятор bc.

Это также работает для поплавков в научном формате, при условии использования заглавной буквы E, например. num1=3.44E6

96 голосов
/ 28 декабря 2011

bash обрабатывает только целые числа но вы можете использовать команду bc следующим образом:

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

Обратите внимание, что знак экспоненты должен быть в верхнем регистре

23 голосов
/ 28 декабря 2011

Лучше использовать 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
20 голосов
/ 08 мая 2014

Решение Pure Bash для сравнения поплавков без экспоненциальной записи, начальных или конечных нулей:

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

Порядок логических операторов имеет значение . Целочисленные части сравниваются как числа, а дробные части преднамеренно сравниваются как строки. Переменные делятся на целые и дробные части, используя этот метод .

Не будет сравнивать числа с целыми числами (без точки).

11 голосов
/ 09 августа 2017

вы можете использовать awk в сочетании с bash, если условие, awk напечатает 1 или 0 , и они будут интерпретироваться выражением if с true или ложь .

if (( $(awk 'BEGIN {print ("'$d1'" >= "'$d2'")}') )); then
    echo "yes"
else 
    echo "no"
fi
5 голосов
/ 15 сентября 2014

Будьте осторожны при сравнении чисел, которые являются версиями пакета, например, проверка, если 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
3 голосов
/ 08 декабря 2017

Конечно, если вам не нужна действительно арифметика с плавающей точкой, просто арифметика, например, в значениях доллара, где всегда есть ровно две десятичных цифры, вы можете просто отбросить точку (умножив ее на 100) и сравнить полученные целые числа.

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

Это, очевидно, требует, чтобы вы были уверены, что оба значения имеют одинаковое количество знаков после запятой.

3 голосов
/ 07 августа 2016

Я использовал ответы отсюда и поместил их в функцию, вы можете использовать ее так:

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
2 голосов
/ 03 июля 2014

Этот скрипт может помочь, когда я проверяю, установлена ​​ли 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
1 голос
/ 19 июня 2018

Используя bashj (https://sourceforge.net/projects/bashj/), мутанта bash с поддержкой java, вы просто пишете (и его легко читать):

#!/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 / java предлагает гораздо больше ...

...