Arra keresem a módját, hogy elmondjam az awk-nak, hogy nagy pontosságú aritmetikát végezzen helyettesítési műveletben. Ez azt jelenti, hogy egy mezőt le kell olvasni egy fájlból, és helyettesíteni kell az érték 1% -os növekedésével. Ott azonban elveszítem a pontosságot. Itt van a probléma egyszerűsített reprodukciója:
$ echo 0.4970436865354813 | awk "{gsub($1, $1*1.1)}; {print}" 0.546748
Itt van egy 16 számjegyem a tizedes pontosság után, de az awk csak hatot ad. A printf használatával ugyanazt az eredményt kapom:
$ echo 0.4970436865354813 | awk "{gsub($1, $1*1.1)}; {printf("%.16G\n", $1)}" 0.546748
Van javaslat a kívánt pontosság elérésére?
Megjegyzések
Válasz
$ echo 0.4970436865354813 | awk -v CONVFMT=%.17g "{gsub($1, $1*1.1)}; {print}" 0.54674805518902947
Vagy inkább itt:
$ echo 0.4970436865354813 | awk "{printf "%.17g\n", $1*1.1}" 0.54674805518902947
valószínűleg a legjobb, amit elérhet. Használja helyette az bc
parancsot tetszőleges precizitás érdekében.
$ echo "0.4970436865354813 * 1.1" | bc -l .54674805518902943
Megjegyzések
- Ha tetszőleges pontosságot szeretne a
AWK
mezőben, használhatja a-M
jelzőt és beállíthatja aPREC
érték nagy számra - @RobertBenson, csak a GNU awk-val és csak a legújabb verziókkal (4.1 vagy újabb, tehát nem a válasz írásakor), és csak akkor, ha az MPFR engedélyezve volt a fordításkor idő.
Válasz
A (GNU) awk (a fordított bignummal) nagyobb pontosság érdekében használja:
$ echo "0.4970436865354813" | awk -M -v PREC=100 "{printf("%.18f\n", $1)}" 0.497043686535481300
A PREC = 100 100 bitet jelent az alapértelmezett 53 bit helyett.
Ha az awk nem érhető el, használja a bc
$ echo "0.4970436865354813*1.1" | bc -l .54674805518902943
Vagy meg kell tanulnod élni az úszók eredendő pontatlanságával.
Eredeti soraiban több kérdés is felmerül:
- Az 1.1 tényező 10% -os növekedés, nem 1% (a-nak kell lennie 1,01 szorzó). 10% -ot fogok használni.
-
A karakterláncból (lebegő) számra történő átalakítási formátumot a CONVFMT adja meg. Alapértelmezett értéke:
%.6g
. Ez az értékeket 6 tizedesjegyre korlátozza (a pont után). Ez a$1
gsub változásának eredményére vonatkozik.$ 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
-
A printf formátum
g
eltávolítja a záró nullákat:$ 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
Mindkét probléma megoldható a következővel:
$ echo "$a" | awk "{printf("%.17g\n", $1*1.1)}" 0.54674805518902947
Vagy
$ echo "$a" | awk -v CONVFMT=%.30g "{gsub($1, $1*1.1)}; {printf("%.17f\n", $1)}" 0.54674805518902947
De ne gondold, hogy ez nagyobb pontosságot jelent. A belső számábrázolás továbbra is dupla méretű úszó. Ez 53 bites pontosságot jelent, és ezzel csak 15 helyes tizedesjegyben lehet biztos, még akkor is, ha sokszor akár 17 számjegy is helyesnek tűnik. Ez a “sa délibáb.”
$ echo "$a" | awk -v CONVFMT=%.30g "{gsub($1, $1*1.1}; {printf("%.30f\n", $1)}" 0.546748055189029469325134868996
A helyes érték:
$ echo "scale=18; 0.4970436865354813 * 1.1" | bc .54674805518902943
Amely akkor is kiszámolandó az (GNU) awk paranccsal, ha a bignum könyvtár a következő fordításban készült:
$ echo "$a" | awk -M -v PREC=100 -v CONVFMT=%.30g "{printf("%.30f\n", $1)}" 0.497043686535481300000000000000
Válasz
Az awk szkriptem nagyobb, mint egy egyvonalas, ezért Stéphane Chazelas és Isaac válaszainak kombinációját használtam:
- Beállítottam a
CONVFMT
változó, amely globálisan gondoskodik a kimenet formázásáról - A
-M
bignum paramétert aPREC
változó
Példa-részlet:
#!/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 { }
Az OP leegyszerűsítette a példáját, de ha a Az awk szkript nem egy egyenes, amelyet nem akarsz printf
sekkel szennyezni, hanem a változóban állítsd be az ilyen formátumot. Hasonlóképpen a pontosság, hogy ne vesszen el a tényleges parancssori meghívásban.
gsub
felesleges. A probléma az, hogy agsub
karakterláncokon működik, nem pedig számokon, ezért először egy átalakítást hajtanak végre aCONVFMT
használatával, és ennek alapértelmezett értéke%.6g
.