awk의 부동 소수점 비교가 예상한 결과를 생성하지 못하는 이유는 무엇입니까?

awk의 부동 소수점 비교가 예상한 결과를 생성하지 못하는 이유는 무엇입니까?

다음 awk 스크립트가 있습니다.

{
    if ($1 > 1000) {
        print $0
    }
}

첫 번째이자 유일한 열의 값이 1000보다 큰 모든 행을 인쇄해야 합니다.

테스트 데이터는 다음과 같습니다.

1,151
1001,055
756,75788

을 사용하면 awk -f my_script.awk my_data다음과 같은 결과가 나타납니다.

1001,055
756,75788

내가 기대하는 것:

1001,055

awk 버전은 다음과 같습니다.

GNU Awk 5.0.0, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.1.2)

내가 뭘 잘못했나요?

편집하다:

의견에서 말했듯이 :

여기서 쉼표는 구분 기호가 아니라 프랑스어에서 사용되는 소수 구분 기호이며 Wikipedia에 따르면 영어를 제외한 모든 표기 시스템에서 사용됩니다.

편집 2: 샘플 데이터에는 열이 하나만 있습니다. 실제 데이터에서 필드 구분 기호는 ";"입니다.

답변1

부인 성명아래의 첫 번째 해결 방법은 원래 문제에 대한 오해로 인해 더 이상 사용되지 않습니다. 일치하는 솔루션은 편집 1과 2를 참조하세요.


awk쉼표는 기본적으로 구분 기호로 인식되지 않습니다. 탭과 공백에 대해서만 이 작업을 수행합니다. 따라서 구분 기호를 명시적으로 정의해야 하며, 그렇지 않으면 awk문자열 값을 비교해야 합니다.

BEGIN {FS=","}
$1 > 1000

조건이 충족되면 한 줄을 인쇄하는 간단한 표기법도 사용하고 있습니다. 이는 더 간단한 코드에 대한 힌트일 뿐입니다.

또는 명령줄에서 구분 기호를 지정합니다.

awk -F,  -f script.awk infile

편집 1다음 사양은 ,소수 구분 기호로 사용됩니다. 소수 구분 기호 awk로 처리되며 소수 구분 기호를 사용하는 로케일은 문제가 되는 경우가 많습니다..

옵션 1의 경우 약간의 트릭을 권장합니다. 정수와 분수를 쉼표로 구분된 별도의 필드로 유지하고 개별적으로 평가합니다.

 BEGIN {FS=","}
 $1==1000 && $2>0 || $1 > 1000

그러면 a) 로캘 사용 시도를 건너뛰고 b) awk-와 -구분 사이를 앞뒤로 번역하는 시도를 건너뜁니다. 단점은 부동 소수점 데이터가 많은 경우 필드 번호가 열 헤더와 일치하지 않을 수 있다는 것입니다. 그러나 실제로 일치하는 줄만 인쇄하는 경우에는 작동하지 않습니다.,.

이렇게 입력

1,151
1001,055
756,75788
1000
1000,00
1000,000001

돌아올 것이다

1001,055
1000,000001

편집 2또 다른, 아마도 더 우아한 옵션은 비교를 위해 첫 번째 필드를 점으로 구분된 부동 소수점으로 변환하는 것입니다.

gensub(/,/,".","g",$1)+0 > 1000

이는 다음과 같이 작동합니다. 필드 1을 문자열로 해석하고, 로 대체하고 ,, .추가하여 0-logic에서 숫자로 만들고 awk, 조건이 true이면 비교하고 인쇄합니다. 장점은 ;필드 구분 기호로 사양을 통해 이 솔루션에서 필드 번호 지정 문제가 발생하지 않는다는 것입니다.


일반적으로 ,가능하면 소수 구분 기호를 사용하지 않는 것이 좋습니다. 물론 이는 데이터를 제공하는 사람에 따라 다릅니다.

답변2

@Ed Morton 및 @steeldriver의 의견을 답변에 추가하려면 GNU awk에게 쉼표를 소수 구분 기호로 처리하고 활성화하거나 --posix/ --use-lc-numeric정의 된 로케일을 사용하도록 할 수 있습니다 -N.

예를 들어:

$ LC_NUMERIC=fi_FI.UTF-8 awk -N '$1 > 1000' data.txt 
1001,055

또는:

$ LC_NUMERIC=fi_FI.UTF-8 awk --posix '$1 > 1000' data.txt 
1001,055

점을 소수 구분 기호로만 처리하는 한, 그런 것은 756,75788숫자로 인식되지 않고 문자열로 인식되며 비교는 문자열 기반입니다. 7이후 정렬 1,이전 정렬 0, 따라서 756,75788> 10001,151< 입니다 1000. (로케일의 대조 규칙도 사용하는지 확실하지 않지만 ,해석 방법에 영향을 미칠 수 있습니다.)

($1 + 0)다음을 사용하여 값을 숫자로 처리하도록 강제할 수 있습니다. 이것은 질문의 데이터에 작동하는 것 같지만 예를 들어 1000,1인쇄 1000되지 않습니다. "1000보다 큼"이 아닌 "최소 1000"을 확인하려면 ($1 + 0) >= 1000소수 부분을 사용하고 무시하면 됩니다.

바라보다:6.1.4.2 로케일 환경이 변환에 영향을 미칩니다그리고6.3.2.1 문자열 유형과 숫자 유형GNU awk 매뉴얼에 있습니다. (나중 페이지의 예는 37< 42비교가 텍스트인지 숫자인지는 중요하지 않기 때문에 어리석은 것입니다.)

관련 정보