awk højpræcisionsaritmetik

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

  • Måske har awk højere opløsning, men det ' er bare din outputformatering afkortes. Brug printf.
  • Ingen ændringer i resultatværdien efter brug af printf. Spørgsmål redigeret i overensstemmelse hermed.
  • Som @manatwork har påpeget, at gsub er unødvendig. Problemet er, at gsub fungerer på strenge, ikke tal, så en konvertering udføres først ved hjælp af CONVFMT, og standardværdien for det er %.6g.
  • @ jw013, Som jeg nævnte i spørgsmålet, kræver mit oprindelige problem gsub, da jeg skal erstatte et tal med en stigning på 1%. Aftalt i det forenklede eksempel er det ikke nødvendigt.

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 indstille PREC 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:

  1. Jeg indstillede CONVFMT variabel, som globalt tager sig af outputformateringen
  2. Jeg bruger også bignum-parameteren -M sammen med PREC 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.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *