대체 연산에서 고정밀 산술을 수행하도록 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>
답변
$ 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 대답의 조합을 사용했습니다.
-
CONVFMT
변수는 출력 형식을 전역 적으로 처리합니다. - 또한 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
로 오염시키고 싶지는 않지만 변수에 이와 같은 형식을 설정합니다. 마찬가지로 정밀도는 실제 명령 줄 호출에서 손실되지 않습니다.
gsub
는 불필요합니다. 문제는gsub
가 숫자가 아닌 문자열에서 작동한다는 것입니다. 따라서 변환은 먼저CONVFMT
를 사용하여 수행되며 기본값은%.6g
.