Jag letar efter ett sätt att berätta awk att göra hög precision aritmetik i en substitutionsoperation. Detta innebär att du läser ett fält från en fil och ersätter det med 1% steg på det värdet. Men jag tappar precision där. Här är en förenklad återgivning av problemet:
$ echo 0.4970436865354813 | awk "{gsub($1, $1*1.1)}; {print}" 0.546748
Här har jag 16 siffror efter decimalprecision men awk ger bara sex. Med printf får jag samma resultat:
$ echo 0.4970436865354813 | awk "{gsub($1, $1*1.1)}; {printf("%.16G\n", $1)}" 0.546748
Några förslag på hur man får önskad precision?
Kommentarer
Svar
$ echo 0.4970436865354813 | awk -v CONVFMT=%.17g "{gsub($1, $1*1.1)}; {print}" 0.54674805518902947
Eller snarare här:
$ echo 0.4970436865354813 | awk "{printf "%.17g\n", $1*1.1}" 0.54674805518902947
är förmodligen det bästa du kan uppnå. Använd bc
istället för godtycklig precision.
$ echo "0.4970436865354813 * 1.1" | bc -l .54674805518902943
Kommentarer
- Om du vill ha godtycklig precision i
AWK
kan du använda-M
-flaggan och ställa inPREC
värde till ett stort antal - @RobertBenson, endast med GNU awk och endast med senaste versioner (4.1 eller högre, så inte vid den tidpunkt då svaret skrevs) och bara när MPFR var aktiverat vid kompilering tid dock.
Svar
För högre precision med (GNU) awk (med bignum sammanställt) använd:
$ echo "0.4970436865354813" | awk -M -v PREC=100 "{printf("%.18f\n", $1)}" 0.497043686535481300
PREC = 100 betyder 100 bitar istället för standard 53 bitar.
Om det här awk inte är tillgängligt, använd bc
$ echo "0.4970436865354813*1.1" | bc -l .54674805518902943
Eller så måste du lära dig att leva med den inneboende oprecisionen av flottor.
I dina ursprungliga rader finns det flera problem: p>
- En faktor på 1,1 är 10% ökning, inte 1% (bör vara a 1,01 multiplikator). Jag använder 10%.
-
Konverteringsformatet från en sträng till ett (flytande) nummer ges av CONVFMT. Standardvärdet är
%.6g
Det begränsar värdena till 6 decimaler (efter punkten). Detta tillämpas på resultatet av gsub-ändringen av$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
tar bort efterföljande nollor:$ 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
Båda problemen kan lösas 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 inte idén att detta innebär högre precision. Den interna numreringen är fortfarande en flottör i dubbel storlek. Det betyder 53 bitar av precision och med det kan du bara vara säker på 15 korrekta decimalsiffror, även om många gånger upp till 17 siffror ser korrekt ut. Det ”sa mirage.
$ echo "$a" | awk -v CONVFMT=%.30g "{gsub($1, $1*1.1}; {printf("%.30f\n", $1)}" 0.546748055189029469325134868996
Det rätta värdet är:
$ echo "scale=18; 0.4970436865354813 * 1.1" | bc .54674805518902943
Vilket kan beräknas också med (GNU) awk om bignum-biblioteket har sammanställts i:
$ echo "$a" | awk -M -v PREC=100 -v CONVFMT=%.30g "{printf("%.30f\n", $1)}" 0.497043686535481300000000000000
Svar
Mitt awk-skript är större än bara ett liner, så jag använde kombinationen av Stéphane Chazelas och Isaacs svar:
- Jag ställde in
CONVFMT
-variabel som globalt tar hand om utdataformateringen - Jag använder också bignum-parametern
-M
tillsammans medPREC
variabel
Exempelavsnitt:
#!/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 förenklade sitt exempel, men om awk-skript är inte ett liner som du inte vill förorena med printf
s, men ställ in formatet så här i variabeln. På samma sätt precision så att den inte går vilse i själva kommandoradsinropet.
gsub
onödigt. Problemet är attgsub
fungerar på strängar, inte på siffror, så en konvertering görs först medCONVFMT
, och standardvärdet för det är%.6g
.