awk yüksek hassasiyetli aritmetik


11

Awk'a bir ikame işleminde yüksek hassasiyetli aritmetik yapmasını söylemenin bir yolunu arıyorum. Bu, bir dosyadan bir alanın okunmasını ve bu değerin% 1'lik bir artışla değiştirilmesini içerir. Ancak, orada hassasiyet kaybediyorum. Sorunun basitleştirilmiş bir şekilde yeniden üretilmesi:

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

Burada, ondalık kesinlikten sonra 16 basamak var ama awk sadece altı veriyor. Printf kullanarak aynı sonucu alıyorum:

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

İstenilen hassasiyeti nasıl alacağınıza dair herhangi bir öneriniz var mı?


Awk daha yüksek çözünürlüğe sahiptir, ancak sadece çıktı biçimlendirmeniz kısalır. Printf kullanın.
dubiousjim

Printf kullandıktan sonra sonuç değerinde değişiklik yok. Soru buna göre düzenlendi.
mkc

@Manatwork'ün işaret ettiği gibi, bu gsubgereksizdir. Sorun gsubsayılarda değil, dizelerde çalışır, bu yüzden önce bir dönüştürme yapılır CONVFMTve bunun için varsayılan değerdir %.6g.
jw013

@ jw013, Soruda belirttiğim gibi, orijinal sorunum gsub gerektiriyor çünkü bir sayıyı% 1'lik bir artışla değiştirmem gerekiyor. Kabul, basitleştirilmiş örnekte, gerekli değildir.
mkc

Yanıtlar:


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

Ya da daha doğrusu burada:

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

muhtemelen elde edebileceğiniz en iyisidir. bcBunun yerine keyfi hassasiyet için kullanın .

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

Eğer keyfi bir hassasiyet AWKistiyorsanız, -Mbayrağı kullanabilir ve PRECdeğeri çok sayıda olarak ayarlayabilirsiniz
Robert Benson

3
@RobertBenson, sadece GNU awk ile ve sadece son sürümlerle (4.1 veya üstü, bu yüzden cevap yazılırken değil) ve sadece MPFR derleme zamanında etkinleştirildiğinde.
Stéphane Chazelas

2

(GNU) awk (derlenmiş bignum ile) ile daha yüksek hassasiyet için:

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

PREC = 100, varsayılan 53 bit yerine 100 bit anlamına gelir.
Bu awk mevcut değilse, bc kullanın

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

Ya da şamandıraların doğal belirsizliğiyle yaşamayı öğrenmeniz gerekecek.


Orijinal satırlarınızda birkaç sorun vardır:

  • 1.1 faktörü,% 1 değil,% 10'luk bir artıştır (1.01 çarpanı olmalıdır). % 10 kullanacağım.
  • Bir dizeden (kayan) sayıya dönüşüm biçimi CONVFMT tarafından verilir. Onun varsayılan değerdir %.6g. Bu, değerleri 6 ondalık basamakla sınırlar (noktadan sonra). Bu, gsub değişikliğinin sonucuna uygulanır $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 biçimi garkadaki sıfırları kaldırır:

    $ 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

    Her iki sorun da şu şekilde çözülebilir:

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

    Veya

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

Ancak bunun daha yüksek hassasiyet anlamına geldiği fikrine kapılmayın. Dahili numara gösterimi hala çift boyutlu bir şamandıradır. Bu, 53 bitlik hassasiyet anlamına gelir ve bununla birlikte, 17 basamağa kadar birçok kez doğru görünse bile, yalnızca 15 ondalık basamaktan emin olabilirsiniz. Bu bir serap.

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

Doğru değer:

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

Eğer bignum kütüphanesi derlenmişse (GNU) awk ile de hesaplanabilir:

$ echo "$a" | awk -M -v PREC=100 -v CONVFMT=%.30g '{printf("%.30f\n", $1)}'
0.497043686535481300000000000000
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.