awk korkean tarkkuuden aritmeettinen

Etsin tapaa kertoa awk: lle korkean tarkkuuden aritmeettinen korvaustoiminto. Tämä tarkoittaa kentän lukemista tiedostosta ja korvaamista 1 prosentin lisäyksellä kyseiseen arvoon. Menetän kuitenkin siellä tarkkuutta. Tässä on yksinkertaistettu ongelman toisto:

 $ echo 0.4970436865354813 | awk "{gsub($1, $1*1.1)}; {print}" 0.546748 

Tässä minulla on 16 numeroa desimaalitarkkuuden jälkeen, mutta awk antaa vain kuusi. Tulostamalla printf-tiedostoa saan saman tuloksen:

$ echo 0.4970436865354813 | awk "{gsub($1, $1*1.1)}; {printf("%.16G\n", $1)}" 0.546748 

Onko sinulla ehdotuksia halutun tarkkuuden saavuttamiseksi?

Kommentit

  • Ehkä awk: llä on korkeampi tarkkuus, mutta se ' vain, että lähdön muotoilu katkeaa. Käytä printf-tiedostoa.
  • Tulosarvossa ei tapahdu muutoksia tulostuksen käytön jälkeen. Kysymystä muokattiin vastaavasti.
  • Kuten @manatwork on huomauttanut, gsub on tarpeeton. Ongelma on, että gsub toimii merkkijonoilla, ei numeroilla, joten muunnos tehdään ensin käyttämällä CONVFMT ja sen oletusarvo on %.6g.
  • @ jw013, Kuten mainitsin kysymyksessä, alkuperäinen ongelmani vaatii gsub: n, koska minun on korvattava luku 1%: n lisäyksellä. Hyväksytty, yksinkertaistetussa esimerkissä sitä ei vaadita.

Vastaa

$ echo 0.4970436865354813 | awk -v CONVFMT=%.17g "{gsub($1, $1*1.1)}; {print}" 0.54674805518902947 

Tai pikemminkin täällä:

$ echo 0.4970436865354813 | awk "{printf "%.17g\n", $1*1.1}" 0.54674805518902947 

on luultavasti paras mitä voit saavuttaa. Käytä mieluummin mielivaltaista tarkkuutta bc.

$ echo "0.4970436865354813 * 1.1" | bc -l .54674805518902943 

Kommentit

  • Jos haluat mielivaltaista tarkkuutta kohdassa AWK, voit käyttää -M -lippua ja asettaa PREC arvo suurelle määrälle
  • @RobertBenson, vain GNU awk: lla ja vain uusimmilla versioilla (4.1 tai uudempi, joten ei silloin, kun vastaus kirjoitettiin) ja vain, kun MPFR oli käytössä käännettäessä aika.

Vastaa

Parempaa tarkkuutta varten (GNU) awk: lla (kun bignum on käännetty sisään):

$ echo "0.4970436865354813" | awk -M -v PREC=100 "{printf("%.18f\n", $1)}" 0.497043686535481300 

PREC = 100 tarkoittaa 100 bittiä 53 oletusarvoisen oletusarvon sijaan.
Jos kyseistä awk-tiedostoa ei ole saatavana, käytä bc

$ echo "0.4970436865354813*1.1" | bc -l .54674805518902943 

Tai sinun on opittava elämään kelluvien luontaisen epätarkkuuden kanssa.


Alkuperäisillä riveilläsi on useita asioita:

  • Kerroin 1,1 on 10%: n kasvu, ei 1% (pitäisi olla a Kerroin 1,01). Käytän 10%.
  • Muunnosmuoto merkkijonosta (kelluvaan) numeroon antaa CONVFMT. Sen oletusarvo on %.6g . Tämä rajoittaa arvot kuuteen desimaalilukuun (pisteen jälkeen). Tätä sovelletaan tulokseen, joka on saatu gsub-muutoksesta $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-muoto g poistaa loppunollat:

    $ 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 

    Molemmat ongelmat voidaan ratkaista seuraavasti:

    $ echo "$a" | awk "{printf("%.17g\n", $1*1.1)}" 0.54674805518902947 

    Tai

    $ echo "$a" | awk -v CONVFMT=%.30g "{gsub($1, $1*1.1)}; {printf("%.17f\n", $1)}" 0.54674805518902947 

Mutta älä ajattele, että tämä tarkoittaa suurempaa tarkkuutta. Sisäinen numeroesitys on edelleen kaksinkertainen kelluva. Tämä tarkoittaa 53 bittiä tarkkuutta ja tällöin voit olla varma vain 15 oikeasta desimaaliluvusta, vaikka monta kertaa jopa 17 numeroa näyttää oikealta. Se ”sa mirage.

$ echo "$a" | awk -v CONVFMT=%.30g "{gsub($1, $1*1.1}; {printf("%.30f\n", $1)}" 0.546748055189029469325134868996 

Oikea arvo on:

$ echo "scale=18; 0.4970436865354813 * 1.1" | bc .54674805518902943 

Mikä voisi lasketaan myös (GNU) awk: lla, jos bignum-kirjasto on koottu seuraavaan:

$ echo "$a" | awk -M -v PREC=100 -v CONVFMT=%.30g "{printf("%.30f\n", $1)}" 0.497043686535481300000000000000 

vastaus

Oma awk-komentosarjani on suurempi kuin vain yksi linja, joten käytin Stéphane Chazelasin ja Isaacin vastausten yhdistelmää:

  1. Asetin CONVFMT muuttuja, joka huolehtii globaalisti lähdön muotoilusta
  2. Käytän myös bignum-parametria -M yhdessä PREC muuttuja

Esimerkkikatkelma:

#!/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 yksinkertaisti hänen esimerkkiään, mutta jos awk-komentosarja ei ole yksi linja, jota et halua saastuttaa sitä printf s: llä, mutta aseta muuttujassa tällainen muoto. Samoin tarkkuus, jotta se ei eksy varsinaiseen komentorivikutsuun.

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *