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

원하는 정밀도를 얻는 방법에 대한 제안?



답변

$ 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

답변

(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

또는 수레의 고유 한 부정확성을 가지고 사는 법을 배워야합니다.


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

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

    $ 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 자리의 정확한 10 진수 만 확신 할 수 있습니다. 신기루입니다.

$ 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