Как выдержать сравнение с числом с плавающей точкой в сценарии оболочки

AT&T/Солярис "Интерфейс Транспортного уровня" (TLI) способ сделать сети TCP/IP имеет специальные файлы как "/dev/tcp" или "/dev/udp". Программист открывает тот специальный файл для получения сокета соответствующего семейства протоколов. Я думаю вот почему, что у Вас должен быть "-lnsl" при компиляции программы, которая использует сокеты на Солярисе: под всем этим это - TLI.

22
17.11.2011, 02:09
11 ответов

Вы могли проверить отдельно целые и дробные части:

#!/bin/bash
min=12.45
val=12.35    
if (( ${val%%.*} < ${min%%.*} || ( ${val%%.*} == ${min%%.*} && ${val##*.} < ${min##*.} ) )) ; then    
    min=$val
fi
echo $min

Как освобождено говорит в комментариях, это работает, только если оба числа имеют дробные части, и обе дробных части имеют то же количество цифр. Вот версия, которая работает на целое число или дробный и любой оператор удара:

#!/bin/bash
shopt -s extglob
fcomp() {
    local oldIFS="$IFS" op=$2 x y digitx digity
    IFS='.' x=( ${1##+([0]|[-]|[+])}) y=( ${3##+([0]|[-]|[+])}) IFS="$oldIFS"
    while [[ "${x[1]}${y[1]}" =~ [^0] ]]; do
        digitx=${x[1]:0:1} digity=${y[1]:0:1}
        (( x[0] = x[0] * 10 + ${digitx:-0} , y[0] = y[0] * 10 + ${digity:-0} ))
        x[1]=${x[1]:1} y[1]=${y[1]:1} 
    done
    [[ ${1:0:1} == '-' ]] && (( x[0] *= -1 ))
    [[ ${3:0:1} == '-' ]] && (( y[0] *= -1 ))
    (( ${x:-0} $op ${y:-0} ))
}

for op in '==' '!=' '>' '<' '<=' '>='; do
    fcomp $1 $op $2 && echo "$1 $op $2"
done
5
27.01.2020, 19:42
  • 1
    Это не может быть зафиксировано без большой работы (попытайтесь выдержать сравнение 0.5 и 0.06). Необходимо использовать инструмент, который уже понимает десятичную запись. –  Gilles 'SO- stop being evil' 17.11.2011, 01:47
  • 2
    Спасибо Gilles, обновленный это для работы в более общем плане, чем более ранняя версия. –  ata 17.11.2011, 07:33
  • 3
    Обратите внимание, что это говорит это 1.00000000000000000000000001 больше, чем 2. –  Stéphane Chazelas 23.07.2014, 15:57
  • 4
    Stéphane является правильным. Это так из-за разрядных пределов в представлении числа удара. Конечно, если Вы хотите больше страдания, Вы могли бы использовать свое собственное представление.... :) –  ata 24.07.2014, 15:55

Bash не понимает арифметику с плавающей точкой. Это рассматривает числа, содержащие десятичную точку как строки.

Используйте awk или до н.э вместо этого.

#!/bin/bash

min=12.45
val=10.35

if [ 1 -eq "$(echo "${val} < ${min}" | bc)" ]
then  
    min=${val}
fi

echo "$min"

Если Вы намереваетесь сделать много математических операций, вероятно, лучше полагаться на Python или жемчуг.

35
27.01.2020, 19:42

Можно использовать цифру-utils пакета для простых манипуляций...

Для более серьезной математики см. эту ссылку... Это описывает несколько опций, например.

  • R/Rscript (GNU R статистическое вычисление и графическая система)
  • октава (главным образом совместимый Matlab)
  • до н.э (GNU до н.э язык калькулятора произвольной точности)

Пример numprocess

echo "123.456" | numprocess /+33.267,%2.33777/
# 67.0395291239087  

A programs for dealing with numbers from the command line

The 'num-utils' are a set of programs for dealing with numbers from the
Unix command line. Much like the other Unix command line utilities like
grep, awk, sort, cut, etc. these utilities work on data from both
standard in and data from files.

Includes these programs:
 * numaverage: A program for calculating the average of numbers.
 * numbound: Finds the boundary numbers (min and max) of input.
 * numinterval: Shows the numeric intervals between each number in a sequence.
 * numnormalize: Normalizes a set of numbers between 0 and 1 by default.
 * numgrep: Like normal grep, but for sets of numbers.
 * numprocess: Do mathematical operations on numbers.
 * numsum: Add up all the numbers.
 * numrandom: Generate a random number from a given expression.
 * numrange: Generate a set of numbers in a range expression.
 * numround: Round each number according to its value.

Вот a bash взлом... Это добавляет ведущий 0 к целому числу для создания строки слева направо сравнением значимый. Эта конкретная часть кода требует, чтобы и минута и val на самом деле имели десятичную точку и по крайней мере одну десятичную цифру.

min=12.45
val=10.35

MIN=0; VAL=1 # named array indexes, for clarity
IFS=.; tmp=($min $val); unset IFS 
tmp=($(printf -- "%09d.%s\n" ${tmp[@]}))
[[ ${tmp[VAL]} < ${tmp[MIN]} ]] && min=$val
echo min=$min

вывод:

min=10.35
12
27.01.2020, 19:42

Для простых вычислений на числах с плавающей точкой (+-*/и сравнения), можно использовать awk.

min=$(echo 12.45 10.35 | awk '{if ($1 < $2) print $1; else print $2}')

Или, если у Вас есть ksh93, или zsh (не колотят), можно использовать встроенную арифметику оболочки, которая поддерживает числа с плавающей точкой.

if ((min>val)); then ((val=min)); fi

Для более усовершенствованных вычислений с плавающей точкой ищите до н.э. Это на самом деле работает над произвольной точностью fixpoint числа.

Для работы над таблицами чисел ищите R (пример).

10
27.01.2020, 19:42

Просто используйте ksh (ksh93 точно) или zsh, который оба исходно поддерживают арифметику с плавающей точкой:

$ cat test.ksh
#!/bin/ksh 
min=12.45
val=10.35    
if (( $val < $min )) ; then    
  min=$val
fi
echo "$min"
$ ./test.ksh
10.35

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

Edit2: Отметьте это ksh93 требует, чтобы переменное содержание согласовывалось с Вашей локалью, т.е. с французской локалью, запятая вместо точки должна использоваться:

...
min=12,45
val=10,35
...

Больше надежного решения состоит в том, чтобы установить локаль в начале сценария, чтобы удостовериться, что это будет работать независимо от локали пользователя:

...
export LC_ALL=C
min=12.45
val=10.35
...
3
27.01.2020, 19:42
  • 1
    Обратите внимание, что вышеупомянутое ksh93 сценарий только работает в локалях, где десятичный разделитель . (так не в половине мира, где десятичный разделитель ,). zsh не имеет той проблемы. –  Stéphane Chazelas 23.07.2014, 09:20
  • 2
    Действительно, ответ, отредактированный для прояснения той мысли. –  jlliagre 23.07.2014, 15:13
  • 3
    Установка LC_NUMERIC не будет работать, если пользователь установил LC_ALL, это также означает, что числа не будут отображены (или введены) в предпочтительном формате пользователя. Посмотрите unix.stackexchange.com/questions/87745/what-does-lc-all-c-do / … для потенциально лучшего подхода. –  Stéphane Chazelas 23.07.2014, 15:20
  • 4
    @StéphaneChazelas устранил проблему LC_NUMERIC. Учитывая синтаксис сценария OP, я предполагаю, что его предпочтительный разделитель . так или иначе. –  jlliagre 23.07.2014, 18:20
  • 5
    Да, но это - локаль пользователя сценария, не локаль автора сценария, который имеет значение. Как автор сценария, необходимо принять во внимание локализацию и ее побочные эффекты. –  Stéphane Chazelas 23.07.2014, 18:29
min=$(echo "${min}sa ${val}d la <a p" | dc)

, который использует калькулятор dc для s вырезания значения для $min в регистре a и d поднимает значение $val на верхнюю часть своего основного исполнительного стека. Затем l помещает содержимое a на вершину стека, и тогда оно выглядит следующим образом:

${min} ${val} ${val}

< выталкивает два верхних элемента из стека и сравнивает их. Таким образом, стек выглядит следующим образом:

${val}

Если верхняя запись была меньше второй, то она выталкивает содержимое a на вершину стека, так что стек выглядит следующим образом:

${min} ${val}

Иначе он ничего не делает, а стек все еще выглядит следующим образом:

${val} 

Тогда он просто p выталкивает верхнюю запись стека.

Итак, для вашей проблемы:

min=12.45
val=12.35
echo "${min}sa ${val}d la <a p" | dc

###OUTPUT

12.35

Но:

min=12.45
val=12.55
echo "${min}sa ${val}d la <a p" | dc

###OUTPUT

12.45
1
27.01.2020, 19:42

Используйте числовое сортировка

Команда Сортировка имеет опцию -G ( - общее числовое сортировка ) Это может быть использовано для сравнения на <, «меньше, чем» или > , «больше, чем», найдя минимальный или максимальный.

Эти примеры находят минимум:

$ printf '12.45\n10.35\n' | sort -g | head -1
10.35

поддерживает электронную запись

, он работает с довольно общей обозначением чисел плавающих точек, как с электронной обоютыми нотами

$ printf '12.45E-10\n10.35\n' | sort -g | head -1
12.45E-10

, обратите внимание на E-10 , делая первое число 0,000000001245 , действительно меньше 10,35 .

может сравниться с Infinity

стандартом с плавающей запятой, IEEE754 , определяет некоторые специальные значения. Для этих сравнений интересными являются инф для бесконечности. Существует также негативная бесконечность; Оба являются хорошо определенными значениями в стандарте.

$ printf 'INF\n10.35\n' | sort -g | head -1
10.35
$ printf '-INF\n10.35\n' | sort -g | head -1
-INF

Чтобы найти максимальное использование сортировка -гру вместо сортировка -G , обратный порядок сортировки:

$ printf '12.45\n10.35\n' | sort -gr | head -1
12.45

Работа сравнения

для реализации < («Менее чем») Сравнение, поэтому его можно использовать в , если и т. Д., Сравните минимум к одному из значений. Если минимум равен значения, по сравнению с текстом , оно меньше, чем другое значение:

$ a=12.45; b=10.35                                    
$ [ "$a" = "$(printf "$a\n$b\n" | sort -g | head -1)" ]
$ echo $?
1
$ a=12.45; b=100.35                                    
$ [ "$a" = "$(printf "$a\n$b\n" | sort -g | head -1)" ]
$ echo $?                                              
0
6
27.01.2020, 19:42

Почему бы не использовать старый, хороший expr?

Пример синтаксиса:

if expr 1.09 '>' 1.1 1>/dev/null; then
    echo 'not greater'
fi

Для выражений true, код выхода expr равен 0, при этом строка '1' отправляется в stdout. Обратный ход для выражений false.

Я проверил это с помощью GNU и FreeBSD 8 expr.

0
27.01.2020, 19:42

Чтобы проверить правильность порядка двух (возможно, дробных) чисел, sort (разумно) переносим:

min=12.45
val=12.55
if { echo $min ; echo $val ; } | sort -n -c 2>/dev/null
then
  echo min is smallest
else
  echo val is smallest
fi

Однако, если вы действительно хотите обновить минимальное значение, вы не нужен , если . Сортируйте числа и всегда используйте первый (наименьший) из них:

min=12.45
val=12.55
smallest=$({ echo $min ; echo $val ; } | sort -n | head -n 1)
echo $smallest
min=$smallest
0
27.01.2020, 19:42

Обычно я делаю аналогичные вещи со встроенным кодом Python:

#!/bin/sh

min=12.45
val=10.35

python - $min $val<<EOF
if ($min > $val):
        print $min
else: 
        print $val
EOF
0
27.01.2020, 19:42
$ min=12.45
$ val=10.35
$ [ "$min" \< "$val" ] && echo $val || echo $min
$ 12.45
$ val=13
$ [ "$min" \< "$val" ] && echo $val || echo $min
$ 13
-1
27.01.2020, 19:42

Теги

Похожие вопросы