awk високої точності арифметики


11

Я шукаю спосіб сказати awk робити високоточну арифметику в операції заміщення. Це передбачає зчитування поля з файлу та заміну його на 1% з цим значенням. Однак я там втрачаю точність. Ось спрощене відтворення проблеми:

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

Тут у мене 16-кратна цифра після десяткової точності, але awk дає лише шість. Використовуючи printf, я отримую той же результат:

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

Будь-які пропозиції щодо того, як отримати бажану точність?


Можливо, awk має більш високу роздільну здатність, але це просто ваше форматування вихідного сигналу. Використовуйте printf.
— сумнівним

Не змінюється значення результату після використання printf. Питання відредаговано відповідно.
— mkc

Як зазначав @manatwork, gsubце зайве. Проблема полягає в gsubроботі на рядках, а не на числах, тому перетворення робиться спочатку за допомогою CONVFMT, а значення за замовчуванням - це %.6g.
— jw013

@ jw013, Як я вже згадував у запитанні, для моєї оригінальної проблеми потрібен gsub, оскільки мені потрібно замінити число з кроком на 1%. Погоджено, у спрощеному прикладі це не потрібно.
— mkc

Відповіді:


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

А точніше тут:

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

це, мабуть, найкраще, що ви можете досягти. Використовуйте bcнатомість для довільної точності.

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

Якщо ви хочете довільної точності, AWKви можете використовувати -Mпрапор і встановити PRECзначення на велику кількість
— Роберт Бенсон

3
@RobertBenson, лише з GNU awk і лише з останніми версіями (4.1 або вище, тому не в той час, коли відповідь була написана), і лише тоді, коли MPFR було включено в час компіляції.
— Стефан Шазелас

2

Для більшої точності (GNU) awk (із складеним бінтумом) використовуйте:

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

PREC = 100 означає 100 біт замість стандартних 53 біт.
Якщо цього awk недоступний, використовуйте bc

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

Або вам потрібно буде навчитися жити з притаманною неточністю поплавків.


У ваших оригінальних рядках є кілька питань:

  • Коефіцієнт 1,1 - це збільшення на 10%, а не на 1% (має бути множник 1,01). Я буду використовувати 10%.
  • Формат перетворення з рядка в (плаваюче) число задається CONVFMT. Його значення за замовчуванням - %.6g. Це обмежує значення на 6 десяткових цифр (після крапки). Це застосовується до результату зміни gsub $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 gвидаляє проміжні нулі:

    $ 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

    Обидва питання можна вирішити за допомогою:

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

    Або

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

Але не розумійте, що це означає більш високу точність. Внутрішнє представлення чисел все ще є поплавцем у подвійному розмірі. Це означає, що 53 біти точності, і ви можете бути впевнені лише у 15 правильних десяткових цифрах, навіть якщо багато разів до 17 цифр виглядають правильно. Це міраж.

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

Правильне значення:

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

Що також можна обчислити за допомогою (GNU) awk, якщо бібліотека bignum складена у:

$ echo "$a" | awk -M -v PREC=100 -v CONVFMT=%.30g '{printf("%.30f\n", $1)}'
0.497043686535481300000000000000
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.