awk nagy pontosságú aritmetika

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

  • Talán az awk nagyobb felbontású, de ' csak a kimenet formázása csonkol. Használja a printf fájlt.
  • A printf használata után az eredmény értéke nem változik. A kérdés ennek megfelelően szerkesztve.
  • Amint arra a @manatwork rámutatott, a gsub felesleges. A probléma az, hogy a gsub karakterláncokon működik, nem pedig számokon, ezért először egy átalakítást hajtanak végre a CONVFMT használatával, és ennek alapértelmezett értéke %.6g.
  • @ jw013, Amint azt a kérdésben említettem, az eredeti problémámhoz gsub szükséges, mivel egy számot 1% -os lépéssel kell helyettesítenem. Egyetértett, az egyszerűsített példában erre nincs szükség.

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 a PREC é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:

  1. Beállítottam a CONVFMT változó, amely globálisan gondoskodik a kimenet formázásáról
  2. A -M bignum paramétert a PREC 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.

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük