Обычный реляционный оператор '<' не работает должным образом awk

Один из возможных ответов с использованием awk:

awk '/name: /{name=substr($0,7)} /count: /{cnt=substr($0,8)} /region:/{print name" "cnt" "substr($0,9)}' input
0
18.03.2020, 23:38
3 ответа
awk -v s=0.2 -v e=0.3 -v d=0.01 '
   BEGIN { m = 1/d }
   { a[int($1*m)]++ }
   END{ e *= m; for(s = int(s*m); s <= e; s++) print s*d, a[s]+0 }
' test_OH.txt

0.2 18
0.21 5
0.22 3
0.23 0
0.24 0
0.25 0
0.26 0
0.27 0
0.28 0
0.29 3
0.3 2

Переменныеs(начало)e(конец )иd(дельта/шаг )можно настроить по мере необходимости.


  1. генерация диапазона путем многократного добавления 0.01— почти хрестоматийный пример того, что НЕ нужно делать с числами с плавающей запятой, потому что 0.01не может быть точно представлено в системе счисления 2, и ошибка будет накапливаться с каждым добавлением.

  2. сканирование всего диапазона для каждой строки неэффективно и бессмысленно.

  3. переменные в awk не должны быть инициализированы как ""или 0.

3
28.04.2021, 23:20

Попробуйте это:

awk '
  BEGIN {for (i=0;i<1.01;i+=0.01) a[i]=0}
  {
    n = int($1 * 100) / 100
    a[n] += 1
  }
  END {for (k=0;k<1.01;k+=0.01) print k,a[k]}'

или это, что я нахожу менее понятным:

awk 'BEGIN {for (i=0;i<1.01;i+=0.01) a[i]=0} {
    for (j=0;j<=1;j+=0.01)
      if(("X" $1 >= "X" j) && ("X" $1 < "X" j+0.01)) {
        a[j]+=1
      }
    }
    END {for (k=0;k<1.01;k+=0.01) print k,a[k]}'

О том, почему оригинал не работал, см. замечание Квазимодо выше.

0
28.04.2021, 23:20

Не уверен, следует ли публиковать это как ответ или комментарий, но вот короткая демонстрация неточности и полученных в этом случае чисел на моей машине:

$ awk 'BEGIN { d = 0.01; printf "%.20f\n", d; for (i = 0; i < 30; i++) a += d; printf "%.20f\n%.20f\n", 0.3, a }'   
0.01000000000000000021
0.29999999999999998890
0.30000000000000009992

Первое число — это то, как фактически сохраняется 0,01.это не точно, так как 1/100 включает коэффициент 1/5 и не может быть представлен в двоичном виде.

Второе — это то, как 0,3 сохраняется, а третье — это 0,01, добавляемое к самому себе 30 раз. (Это даже не то же самое, что 0.01 * 30, потому что, я полагаю, на каждом шаге есть промежуточное округление.)

В других ответах есть решения, проголосуйте за них.

0
28.04.2021, 23:20

Теги

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