Jeg leder efter en måde at fortælle awk om at lave højpræcisionsberegning i en substitutionsoperation. Dette indebærer at læse et felt fra en fil og erstatte det med 1% forøgelse af denne værdi. Jeg mister dog præcisionen der. Her er en forenklet gengivelse af problemet:
$ echo 0.4970436865354813 | awk "{gsub($1, $1*1.1)}; {print}" 0.546748
Her har jeg et 16 cifret efter decimal præcision, men awk giver kun seks. Ved hjælp af printf får jeg det samme resultat:
$ echo 0.4970436865354813 | awk "{gsub($1, $1*1.1)}; {printf("%.16G\n", $1)}" 0.546748
Eventuelle forslag til, hvordan man får den ønskede præcision?
Kommentarer
Svar
$ echo 0.4970436865354813 | awk -v CONVFMT=%.17g "{gsub($1, $1*1.1)}; {print}" 0.54674805518902947
Eller rettere her:
$ echo 0.4970436865354813 | awk "{printf "%.17g\n", $1*1.1}" 0.54674805518902947
er sandsynligvis det bedste, du kan opnå. Brug bc
i stedet for vilkårlig præcision.
$ echo "0.4970436865354813 * 1.1" | bc -l .54674805518902943
Kommentarer
- Hvis du vil have vilkårlig præcision i
AWK
kan du bruge-M
flag og indstillePREC
værdi til et stort antal - @RobertBenson, kun med GNU awk og kun med nyere versioner (4.1 eller derover, så ikke på det tidspunkt svaret blev skrevet) og kun når MPFR var aktiveret ved kompilering tid dog.
Svar
For højere præcision med (GNU) awk (med bignum kompileret i) brug:
$ echo "0.4970436865354813" | awk -M -v PREC=100 "{printf("%.18f\n", $1)}" 0.497043686535481300
PREC = 100 betyder 100 bits i stedet for standard 53 bit.
Hvis dette awk ikke er tilgængeligt, skal du bruge bc
$ echo "0.4970436865354813*1.1" | bc -l .54674805518902943
Eller du bliver nødt til at lære at leve med den iboende upræcision af floats.
I dine originale linjer er der flere problemer:
- En faktor på 1,1 er 10% stigning, ikke 1% (skal være a 1,01 multiplikator). Jeg bruger 10%.
-
Konverteringsformatet fra en streng til et (flydende) nummer er givet af CONVFMT. Standardværdien er
%.6g
Dette begrænser værdierne til 6 decimaler (efter prikken). Det anvendes på resultatet af gsub-ændringen af$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-formatet
g
fjerner efterfølgende nuller:$ 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
Begge problemer kunne løses med:
$ echo "$a" | awk "{printf("%.17g\n", $1*1.1)}" 0.54674805518902947
Eller
$ echo "$a" | awk -v CONVFMT=%.30g "{gsub($1, $1*1.1)}; {printf("%.17f\n", $1)}" 0.54674805518902947
Men får ikke ideen om, at dette betyder højere præcision. Den interne talrepræsentation er stadig en svømmer i dobbelt størrelse. Det betyder 53 bit præcision, og med det kunne du kun være sikker på 15 korrekte decimalcifre, selvom op til 17 cifre mange gange ser rigtige ud. At “sa mirage.
$ echo "$a" | awk -v CONVFMT=%.30g "{gsub($1, $1*1.1}; {printf("%.30f\n", $1)}" 0.546748055189029469325134868996
Den korrekte værdi er:
$ echo "scale=18; 0.4970436865354813 * 1.1" | bc .54674805518902943
Hvilket kunne beregnes også med (GNU) awk, hvis bignum-biblioteket er blevet samlet i:
$ echo "$a" | awk -M -v PREC=100 -v CONVFMT=%.30g "{printf("%.30f\n", $1)}" 0.497043686535481300000000000000
Svar
Mit awk-script er større end bare en liner, så jeg brugte kombinationen af Stéphane Chazelas og Isaacs svar:
- Jeg indstillede
CONVFMT
variabel, som globalt tager sig af outputformateringen - Jeg bruger også bignum-parameteren
-M
sammen medPREC
variabel
Eksempel på uddrag:
#!/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 forenklede sit eksempel, men hvis awk-script er ikke en linie, du ikke vil forurene det med printf
s, men indstil formatet som dette i variablen. Ligeledes præcisionen, så den ikke går tabt i selve kommandolinjeanmeldelsen.
gsub
er unødvendig. Problemet er, atgsub
fungerer på strenge, ikke tal, så en konvertering udføres først ved hjælp afCONVFMT
, og standardværdien for det er%.6g
.