Проблема с плавающей точкой сравнения - PullRequest
0 голосов
/ 28 августа 2011

Я пытаюсь проверить, равно ли значение, которое я читаю из текстового файла, нулю:

[[ $(echo $line | cut -d" " -f5) -gt 0 ]] && [[ $(echo $line | cut -d" " -f7 | bc -l) -eq 0 ]]

С первым условием проблем нет, потому что f5 - целые числа. Проблема возникает из второго условия. Я получаю это сообщение об ошибке:

[[: 1.235: syntax error: invalid arithmetic operator (error token is ".235")

Я пробовал несколько предложений, которые я нашел на разных форумах, таких как использование echo $line | cut -d" " -f7 | bc -l с двойными кавычками и без них и т. Д. Однако ошибка сохраняется f7 является положительным числом и задается с тремя десятичными знаками. Удаление десятичных знаков или приближение не вариант, потому что мне нужно, чтобы результат был точно нулевым (0,000).

Ответы [ 2 ]

2 голосов
/ 28 августа 2011

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

В этом конкретном случае вам на самом деле не нужно сравнивать числа с плавающей запятой, потому что вы просто проверяете, представляет ли какой-то текст конкретное число.Поскольку вы находитесь в оболочке, вы можете либо использовать обычное сравнение строк с «0,000» (при условии, что ваши данные округлены таким образом), либо использовать регулярные выражения с grep / egrep.Что-то вроде

egrep -q '0(|\.0+)'

Будет соответствовать 0, 0,0, 0,00 и т. Д. И будет выходить, указывая на успех или неудачу, что можно использовать в операторе if окружения:

if cut and pipe soup | egrep ... ; then
  ...
fi
1 голос
/ 28 августа 2011

Вместо этого используйте сравнение строк. Заменить:

-eq 0

с:

= '0.000'

TZ:

Сценарий из комментария:

for clus in $(ls *.cluster) ; do
    while read line ; do
        if [[ $(echo $line | cut -d" " -f11) -gt 0 ]] && [[ "$(echo $line | cut -d" " -f15 | bc -l)" = '0.000' ]] ; then
            cat $(echo $line | cut -d" " -f6).pdb >> test/$(echo $line | cut -d" " -f2)_pisa.pdb
        fi
    done < $clus
done

Моя псевдо-Python интерпретация:

for clus in *.cluster:
    for line in clus:
        fields = line.split(' ')
        # field numbers are counting from 1 as in cut
        if int(field 11) > 0 and str(field 15) == '0.000':
            fin_name = (field 6) + '.pdb'
            fout_name = (field 2) + '_pisa.pdb'
            cat fin_name >> fout_name

Это то, что вы хотели?

...