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
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 asettaaPREC
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ää:
- Asetin
CONVFMT
muuttuja, joka huolehtii globaalisti lähdön muotoilusta - 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.
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
.