왜 gawk는 (때때로?) 2.0e-318 > 2.0이라고 생각합니까?

왜 gawk는 (때때로?) 2.0e-318 > 2.0이라고 생각합니까?

데이터 열의 최대값을 찾기 위해 gawk를 사용하려고 합니다.

gawk 'BEGIN{max=0} {if($1>0+max) max=$1} END {print max}' dataset.dat

다음과 같습니다 dataset.dat.

2.0
2.0e-318

명령의 출력은 다음과 같습니다.

2.0e-318

당연히 2보다 작습니다.

내 실수는 어디에 있습니까?

편집하다

흥미롭게도 입력 파일의 행을 바꾸면 출력은 다음과 같습니다.

2.0

편집 2

내 gawk 버전은 GNU Awk 4.2.1, API: 2.0(GNU MPFR 4.0.2, GNU MP 6.1.2)입니다.

답변1

2e-318awk에서는 이러한 작은 숫자( )를 처리하는 데 몇 가지 문제가 있습니다.

  • 먼저, 입력을 사용하기 전에 숫자로 변환해야 합니다. 이는 일반적으로 0을 추가하여 수행됩니다. 따라서 다음과 같은 것이 필요합니다.

    val=0+$1
    
  • 2. 일반 배정밀도 부동 소수점 숫자(53비트 가수 및 11비트 지수)지수의 11비트 너비를 사용하면 10e-308에서 10e308 사이의 숫자를 표현할 수 있습니다.따라서 일반 부동 소수점 숫자는 이러한 숫자를 표현할 수 없습니다.

    $ echo '1e-307 1e-308' | awk '{print $1,$1+0,$2,$2+0}'
    1e-307 1e-307 1e-308 0
    

    기본적으로 GNU awk는 다음 (일반) 값을 허용하지 않습니다 1e-308.

  • 셋째, 기본 변환 형식(CNVFMT 및 OFMT) awk은 으로 설정됩니다 "%.6g". 유효 숫자가 6개를 초과하는 숫자는 잘립니다. 더 중요한 번호를 확인하려면 요청하세요. 15 와 같습니다 %.15g(53자리 가수에 17 이상을 요구하지 마십시오. 거짓말을 할 수도 있습니다).

  • max넷째, 첫 번째 입력에 첫 번째 값을 설정하는 것이 좋습니다 . 입력 최대값이 음수이면 최대값을 0으로 설정하면 실패합니다.


GNU awk를 사용하고 임의의 정밀도로 컴파일된 경우 다음을 사용할 수 있습니다.

$ printf '%s\n' 2e-318 2e-317 2e-307 2e-308 2e-319 | 
    awk -M -v PREC=100     'BEGIN{OFMT="%.15g"};
        {val=0+$1};
        NR==1{max=val};
        {print($1,val,max)};
        val>max{max=val}
        END{print max}'

2e-318 2e-318 2e-318
2e-317 2e-317 2e-318
2e-307 2e-307 2e-317
2e-308 2e-308 2e-307
2e-319 2e-319 2e-307
2e-307 

또는 사용 사례에 맞게 단순화되었습니다.

awk -M -v PREC=100 '
    BEGIN{OFMT="%.15g"};    # allow more than 6 figures
    {val=0+$1};             # convert input to a (float) number.
    NR==1{max=val};         # On the first line, set the max value.
    val>max{max=val}        # On every entry keep track of the max.
    END{print max}          # At the end, print the max.
    '  file                 # file with input (one per line).

답변2

숫자 변환을 강제하려면 각 $1의 접두사로 0+가 필요합니다. max에는 0+가 필요하지 않습니다. 저장 시 이미 숫자로 변환되었습니다.

Paul--) AWK='
> BEGIN { max = 0; }
> 0+$1 > max { max = 0 + $1; }
> END { print max; }
> '
Paul--) awk "${AWK}" <<[][]
> 2.0
> 2.0e-318
> [][]
2
Paul--) awk "${AWK}" <<[][]
> 2.0e-318
> 2.0
> [][]
2

관련 정보