awk 고정밀 산술

대체 연산에서 고정밀 산술을 수행하도록 awk에게 지시하는 방법을 찾고 있습니다. 여기에는 파일에서 필드를 읽고 해당 값에 대해 1 % 증분으로 대체하는 것이 포함됩니다. 그러나 나는 거기에서 정밀도를 잃고 있습니다. 다음은 문제의 단순화 된 재현입니다.

 $ echo 0.4970436865354813 | awk "{gsub($1, $1*1.1)}; {print}" 0.546748 

여기서 십진 정밀도 뒤에 16 자리 숫자가 있지만 awk는 6 자리 만 제공합니다. printf를 사용하여 동일한 결과를 얻었습니다.

$ echo 0.4970436865354813 | awk "{gsub($1, $1*1.1)}; {printf("%.16G\n", $1)}" 0.546748 

원하는 정밀도를 얻는 방법에 대한 제안 사항이 있습니까?

댓글

h3>

  • 아마도 awk의 해상도가 더 높지만 ' 출력 형식이 잘립니다. printf를 사용하십시오.
  • printf를 사용한 후 결과 값이 변경되지 않습니다. 그에 따라 질문이 수정되었습니다.
  • @manatwork가 지적했듯이 gsub는 불필요합니다. 문제는 gsub가 숫자가 아닌 문자열에서 작동한다는 것입니다. 따라서 변환은 먼저 CONVFMT를 사용하여 수행되며 기본값은 %.6g.
  • @ jw013, 질문에서 언급했듯이 원래 문제는 1 % 증가로 숫자를 대체해야하므로 gsub가 필요합니다. 동의합니다. 단순화 된 예에서는 필수가 아닙니다.

답변

$ echo 0.4970436865354813 | awk -v CONVFMT=%.17g "{gsub($1, $1*1.1)}; {print}" 0.54674805518902947 

또는 여기에 있습니다.

$ echo 0.4970436865354813 | awk "{printf "%.17g\n", $1*1.1}" 0.54674805518902947 

가 달성 할 수있는 최선의 방법 일 것입니다. 임의 정밀도 대신 bc를 사용하세요.

$ echo "0.4970436865354813 * 1.1" | bc -l .54674805518902943 

댓글

  • AWK에서 임의의 정밀도를 원하는 경우 -M 플래그를 사용하고 PREC 값을 큰 수로
  • @RobertBenson, GNU awk 만 사용하고 최신 버전 만 사용 (4.1 이상, 따라서 답변이 작성 될 때가 아님) 및 컴파일시 MPFR이 활성화 된 경우에만 그래도 시간이 있습니다.

Answer

(GNU) awk (bignum 컴파일 됨)를 사용하여 더 높은 정밀도를 얻으려면 다음을 사용하십시오.

$ echo "0.4970436865354813" | awk -M -v PREC=100 "{printf("%.18f\n", $1)}" 0.497043686535481300 

PREC = 100은 기본 53 비트 대신 100 비트를 의미합니다.
해당 awk를 사용할 수없는 경우 bc를 사용하십시오.

$ echo "0.4970436865354813*1.1" | bc -l .54674805518902943 

또는 부동의 본질적인 부정확성에 대해 배워야합니다.


원래 줄에는 몇 가지 문제가 있습니다.

p>

  • 1.1의 계수는 1 %가 아니라 10 % 증가입니다 ( 1.01 승수). 10 %를 사용하겠습니다.
  • 문자열에서 (부동) 숫자로의 변환 형식은 CONVFMT에 의해 제공됩니다. 기본값은 %.6g입니다. . 이는 값을 소수점 이하 6 자리 (점 뒤)로 제한합니다. 이는 $1의 gsub 변경 결과에 적용됩니다.

    $ a="0.4970436865354813" $ echo "$a" | awk "{printf("%.16f\n", $1*1.1)}" 0.5467480551890295 $ echo "$a" | awk "{gsub($1, $1*1.1)}; {printf("%.16f\n", $1)}" 0.5467480000000000 
  • printf 형식 g는 후행 0을 제거합니다.

    $ echo "$a" | awk "{gsub($1, $1*1.1)}; {printf("%.16g\n", $1)}" 0.546748 $ echo "$a" | awk "{gsub($1, $1*1.1)}; {printf("%.17g\n", $1)}" 0.54674800000000001 

    두 문제는 다음과 같이 해결할 수 있습니다.

    $ echo "$a" | awk "{printf("%.17g\n", $1*1.1)}" 0.54674805518902947 

    또는

    $ echo "$a" | awk -v CONVFMT=%.30g "{gsub($1, $1*1.1)}; {printf("%.17f\n", $1)}" 0.54674805518902947 

그러나 이것이 더 높은 정밀도를 의미한다는 생각은하지 마십시오. 내부 숫자 표현은 여전히 이중 크기의 부동 소수점입니다. 이는 53 비트의 정밀도를 의미하며 최대 17 자리까지 여러 번 정확 해 보이더라도 15 자리의 정확한 십진수 만 확인할 수 있습니다. 이것은 신기루입니다.

$ echo "$a" | awk -v CONVFMT=%.30g "{gsub($1, $1*1.1}; {printf("%.30f\n", $1)}" 0.546748055189029469325134868996 

올바른 값 :

$ echo "scale=18; 0.4970436865354813 * 1.1" | bc .54674805518902943 

bignum 라이브러리가 컴파일 된 경우 (GNU) awk로도 계산됩니다.

$ echo "$a" | awk -M -v PREC=100 -v CONVFMT=%.30g "{printf("%.30f\n", $1)}" 0.497043686535481300000000000000 

Answer

내 awk 스크립트가 한 줄로 된 것보다 크기 때문에 저는 Stéphane Chazelas”s와 Isaac “s 대답의 조합을 사용했습니다.

  1. CONVFMT 변수는 출력 형식을 전역 적으로 처리합니다.
  2. 또한 iv id =와 함께 bignum 매개 변수 -M를 사용합니다. “7a8358f014″>

변수

예시 스 니펫 :

#!/usr/bin/awk -M -f BEGIN { FS="<|>" CONVFMT="%.18g" PREC=100 } { if ($2 == "LatitudeDegrees") { CORR = $3 // redacted specific corrections print(" <LatitudeDegrees>" CORR "</LatitudeDegrees>"); } else if ($2 == "LongitudeDegrees") { CORR = $3 // redacted specific corrections print(" <LongitudeDegrees>" CORR "</LongitudeDegrees>"); } else { print($0); } } END { } 

OP는 그의 예를 단순화했지만 awk 스크립트는 printf로 오염시키고 싶지는 않지만 변수에 이와 같은 형식을 설정합니다. 마찬가지로 정밀도는 실제 명령 줄 호출에서 손실되지 않습니다.

답글 남기기

이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다