소수 값을 합산할 때 awk sum에 완전한 소수가 필요합니다.

소수 값을 합산할 때 awk sum에 완전한 소수가 필요합니다.

다음 test.txt 파일이 있습니다.

var,value
a,1.1234
b,1.7896749
c,2.4982
d,1.2976232

다음 명령을 사용할 때 -

awk -F ',' '{SUM+=$2}END{print SUM}' test.txt

그것은 인쇄한다6.7089

그러나 모든 소수 자릿수가 보존된 결과는 다음과 같습니다.6.7088981 이 특정한 경우뿐만 아니라 일반적으로 결과의 모든 소수 자릿수가 인쇄되도록 명령을 작성하려면 어떻게 해야 합니까? 예를 들어 결과에 소수점 이하 10자리가 있는 경우 소수점 이하 10자리를 모두 인쇄해야 합니까? 결과에 소수점 이하 5자리만 있는 경우 소수점 5자리만 인쇄해야 합니다. 제가 사용하는 운영 체제는 Red Hat Enterprise Linux Server 7.7입니다.

답변1

OFMTprintf인쇄할 때, 정수가 아닌 숫자는 (기본적으로) 형식 사양이 포함된 특수 변수를 사용하여 10진수 문자열 표현으로 변환됩니다 %.6g. 이를 변경하여 %.17gIEEE 754 이중 정밀도 이진 부동 소수점 숫자( awk대부분의 시스템에서 대부분의 구현에서 내부적으로 사용됨)의 최대 정밀도를 얻을 수 있습니다. 다른 변수( CONVFMT)는 부동 소수점 숫자가 암시적으로 문자열로 변환되는 다른 경우(예: 숫자를 다른 내용과 연결하는 경우)에 사용됩니다.

이러한 double을 사용하면 더 높은 정밀도를 얻을 수 없으며 17 이상으로 올라가는 것은 의미가 없습니다. 이미 17을 사용하고 있으면 일부 아티팩트가 나타날 수 있습니다. 그렇게 많은 정밀도가 필요하지 않은 경우 유효 숫자 15개가 더 나을 수 있습니다.

$ awk -v OFMT=%.17g -F ',' '{SUM+=$2};END{print SUM}' < file
6.7088981000000008
$ awk -v OFMT=%.15g -F ',' '{SUM+=$2};END{print SUM}' < file
6.7088981

인쇄된 모든 부동 소수점 숫자에 영향을 주지만 원하는 정밀도로 숫자를 인쇄하는 데 직접 사용할 OFMT수도 있습니다 .printf

$ awk  -F ',' '{SUM+=$2};END{printf "%.15g\n", SUM}' < file
6.7088981

버전 4.1.0부터 GNU 구현은 awk임의 정밀도 산술 지원을 통해 컴파일될 수도 있습니다(참고자료 참조 info gawk 'Arbitrary Precision Arithmetic'). 시스템에 이런 일이 발생하면 다음을 수행할 수도 있습니다.

gawk -M -v PREC=256 -v OFMT=%.60g -F ',' '{SUM+=$2};END{print SUM}' < file

예:

$ printf 'x,%s\n' 1 1000000000000000000000000000000000.00000000001 |
> gawk -v OFMT=%.15g -F ',' '{SUM+=$2};END{print SUM}'
999999999999999945575230987042816
$ printf 'x,%s\n' 1 1000000000000000000000000000000000.00000000001 |
> gawk -M -v PREC=256 -v OFMT=%.60g -F ',' '{SUM+=$2};END{print SUM}'
1000000000000000000000000000000001.00000000001

여기서 또 다른 접근 방식은 다음을 사용하는 것입니다 bc(숫자가 항상 다음과 같이 표현된다고 가정( 예: 0.001아님 1e-3)).

<file tail -n+2 | # skip header
  cut -d, -f2   | # extract second field
  paste -sd + - | # join input lines with +
  bc

다음 자릿수는 .모든 입력 레코드의 최대 자릿수가 됩니다.

답변2

GNU 데이터 혼합원하는 정밀도로 숫자를 표시하려면 기본 출력 설정을 사용하십시오.

$ datamash --header-in -t, sum 2 < test.txt
6.7088981

OFMT또는 더 정확하게 하려면 다른 awk를 사용하십시오.

$ awk -F, -v OFMT='%.10g' '{sum += $2} END { print sum }' test.txt
6.7088981

하지만 보세요부동 소수점 연산에 문제가 있나요?. 부동 소수점 숫자를 10진법으로 표시할 때 소수점 뒤의 자릿수가 (대부분의) 컴퓨터에서 사용하는 IEEE754, 2진법 표현과 항상 일치하는 것은 아닙니다.

답변3

이미 논의한 바와 같이, 부동 소수점 산술은 직관적인 답을 얻으려고 할 때 문제가 되지만, 입력할 수 있는 경우 "." 앞에 최대 3자리만 있을 수 있습니다. 9 이후에는 문자열 연산을 사용하여 숫자를 소수로 변환한 다음 이를 추가하여 부동 소수점 산술 문제를 방지한 다음 인쇄하기 전에 결과를 다시 FP로 변환할 수 있습니다. 예를 들면 다음과 같습니다.

$ cat tst.awk
BEGIN {
    FS = ","
    bef = 3
    aft = 9
}
NR>1 {
    split($2,f,".")
    val = sprintf("%*s%-*s",bef,f[1],aft,f[2])
    gsub(/ /,0,val)
    sum += val
}
END {
    sub(".{"aft"}$",".&",sum)
    sub(/0+$/,"",sum)
    print sum
}

$ awk -f tst.awk file
6.7088981

3 및/또는 9가 충분히 크지 않은 경우 다른 숫자를 선택하거나 2단계 방법을 수행하여 첫 번째 단계에서 각 숫자의 최대값을 알아냅니다. 예를 들면 다음과 같습니다.

$ cat tst.awk
BEGIN { FS = "," }
FNR==1 { next }
{ split($2,f,".") }
NR==FNR {
    bef = (length(f[1]) > bef ? length(f[1]) : bef)
    aft = (length(f[2]) > aft ? length(f[2]) : aft)
    next
}
{
    val = sprintf("%*s%-*s",bef,f[1],aft,f[2])
    gsub(/ /,0,val)
    sum += val
}
END {
    sub(".{"aft"}$",".&",sum)
    sub(/0+$/,"",sum)
    print sum
}

$ awk -f tst.awk file file
6.7088981

관련 정보