Подтвердить что ты не робот

Как получить bc для обработки чисел в научной (так называемой экспоненциальной) нотации?

bc не любит числа, выраженные в научной нотации (как экспоненциальная нотация).

$ echo "3.1e1*2" | bc -l
(standard_in) 1: parse error

но мне нужно использовать его для обработки нескольких записей, выраженных в этих обозначениях. Есть ли способ получить bc для понимания экспоненциальной нотации? Если нет, что я могу сделать, чтобы перевести их в формат, который bc будет понимать?

4b9b3361

Ответ 1

К сожалению, bc не поддерживает научную нотацию.

Однако его можно перевести в формат, который bc может обрабатывать, используя sed:
Используя

value=`echo ${value} | sed -e 's/[eE]+*/\\*10\\^/'`

вы можете заменить "e" (или "e +", если показатель положителен) с "* 10 ^", который bc будет быстро понимать. Это работает, даже если показатель отрицателен или число впоследствии умножается на другую мощность и позволяет отслеживать значимые цифры.

Изменить: благодаря jwpat7 и Paul Tomblin для уточнения аспектов синтаксиса sed.

Edit:

Как указывал Ормаай, это также можно сделать в чистом bash. Просто используйте

value=${value/[eE]+*/*10^}

чтобы выполнить то же самое, что и выше.

Ответ 2

Для этого можно использовать awk; например,

awk '{ print +$1, +$2, +$3 }' <<< '12345678e-6 0.0314159e2 54321e+13'

производит (через формат awk по умолчанию%.6g), например, 12.3457 3.14159 543210000000000000
в то время как команды, подобные следующим двум, выводят вывод, показанный после каждого, учитывая, что файл edata содержит данные, как показано ниже.

$ awk '{for(i=1;i<=NF;++i)printf"%.13g ",+$i; printf"\n"}' < edata`
31 0.0312 314.15 0 
123000 3.1415965 7 0.04343 0 0.1 
1234567890000 -56.789 -30 

$ awk '{for(i=1;i<=NF;++i)printf"%9.13g ",+$i; printf"\n"}' < edata
       31    0.0312    314.15         0 
   123000 3.1415965         7   0.04343         0       0.1 
1234567890000   -56.789       -30 


$ cat edata 
3.1e1 3.12e-2 3.1415e+2 xyz
123e3 0.031415965e2 7 .4343e-1 0e+0 1e-1
.123456789e13 -56789e-3 -30

Кроме того, в отношении решений, использующих sed, лучше удалить знак плюс в формах, таких как 45e+3, в то же время, что и e, с помощью регулярного выражения [eE]+*, а не в отдельном sed выражение. Например, на моей Linux-машине с GNU sed версии 4.2.1 и bash версии 4.2.24, команды
sed 's/[eE]+*/*10^/g' <<< '7.11e-2 + 323e+34'
sed 's/[eE]+*/*10^/g' <<< '7.11e-2 + 323e+34' | bc -l
производить продукцию
7.11*10^-2 + 323*10^34
3230000000000000000000000000000000000.07110000000000000000

Ответ 3

Позвольте мне попробовать суммировать существующие ответы с комментариями для каждого ниже:

  • (a) Если вам действительно нужно использовать bc для вычислений с произвольной точностью - как это делает OP - используйте OP own умный подход, который текстово переформатирует научную нотацию эквивалентным выражением, которое понимает bc.

  • Если потенциальная потеря точности не является проблемой,

    • (б) рассмотрите возможность использования awk или perl в качестве bc альтернатив; как изначально понимают научную нотацию, как показано в ответе jwpat7 для awk.
    • (c) рассмотрите возможность использования printf '%.<precision>f' просто текстового преобразования в регулярное представление с плавающей запятой (десятичные дроби без e/e) (a решение, предложенное в удаленном сообщении ormaaj).

(a) Преобразование научной нотации в эквивалентное выражение bc

(Обтекаемая, надежная версия собственного ответа OP.)

Преимущество этого решения состоит в том, что точность сохраняется: текстовое представление преобразуется в эквивалентное текстовое представление, которое bc может понять, а сам bc способен к вычислениям с произвольной точностью.

Примечание. Предполагается, что ваш ввод представляет собой одно число в научной нотации (хранится в $value):

 value="($(sed 's/[eE]+\{0,1\}/*10^/g' <<<"$value"))"

Обратите внимание, что sed по умолчанию использует основные регулярные выражения, где + не является специальным символом и, наоборот, логикой дублирования "0 или 1 вхождения" (+ в расширенных регулярных выражениях ) должны быть реализованы как \{0,1\}. Другими словами: основное регулярное выражение +\{0,1\} эквивалентно расширенному регулярному выражению \++.

Примеры:

  • 1e2(1*10^2)
  • .3e+1(.3*10^1)
  • 2.5e-2(2.5*10^-2)

Полученное выражение можно безопасно использовать в команде bc.

Обратите внимание на то, что выражение должно быть заключено в круглые скобки, чтобы гарантировать, что из-за приоритета оператора при вложении полученного выражения в вычисление не возникнет никаких проблем - см. комментарий Эрика Аронисты к собственному ответу OP для обсуждение.

Примечание. Если вы хотите переформатировать весь расчет, содержащий потенциально несколько чисел в научной нотации, все усложняется - см. комментарии к собственному ответу OP для обсуждения.


(b) Использование awk или perl вместо bc в качестве калькулятора

Примечание. Следующие подходы предполагают использование встроенной поддержки для значений с плавающей запятой с двойной точностью в awk и perl. Как и в случае с арифметикой с плавающей запятой,
", учитывая любое фиксированное количество бит, большинство вычислений с действительными числами будут давать количества, которые не могут быть точно представлены с использованием этого количества бит. Поэтому результат вычисления с плавающей запятой часто должен округляться, чтобы поместиться обратно в его конечный Это ошибка округления является характерной особенностью вычисления с плавающей запятой." (http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html)

Тем не менее,

  • GNU awk предлагает возможность построения с поддержкой арифметики произвольной точности - см. https://www.gnu.org/software/gawk/manual/html_node/Gawk-and-MPFR.html; однако типичные дистрибутивы не включают эту поддержку.

  • Perl предлагает дополнительную десятичную поддержку с произвольной точностью через пакет Math::BigFloat - см. https://metacpan.org/pod/Math::BigFloat

AWK

awk изначально понимает десятичную экспоненциальную (научную) нотацию.
(Обычно вы должны использовать только числа_дискама, потому что реализации awk отличаются друг от друга относительно того, поддерживают ли они числовые литералы с другими базами.)

awk 'BEGIN { print 3.1e1 * 2 }'  # -> 62

Если вы используете функцию по умолчанию print, переменная OFMT управляет выходным форматом с помощью строки формата printf; (значение POSIX) по умолчанию - %.6g, что означает 6 значащих цифр, в число которых входят цифры целой части.

Обратите внимание, что если номер в научной нотации поставляется как вход (в отличие от буквальной части awk-программы), вы должны добавить +0, чтобы заставить его использовать выходной формат по умолчанию, если он используется сам по себе с print:

В зависимости от вашей локали и реализации awk, которую вы используете, вам может потребоваться заменить десятичную точку (.) на подходящий для локали символ основания, например , в немецком языке; применяется к BSD awk, mawk и к GNU awk с опцией --posix.

awk '{ print $1+0 }' <<<'3.1e1' # -> 31; without `+0`, output would be the same as input

Изменение переменной OFMT изменяет формат вывода по умолчанию (для чисел с дробными частями (эффективные) целые числа всегда выводятся как таковые).
В качестве альтернативы используйте функцию printf с явным форматом вывода:

awk 'BEGIN { printf "%.4f", 3.1e1 * 2.1234 }' # -> 65.8254

Perl

perl тоже изначально понимает десятичную экспоненциальную (научную) нотацию.

Примечание. Perl, в отличие от awk, недоступен на всех платформах, подобных POSIX, по умолчанию; кроме того, он не такой легкий, как awk.
Тем не менее, он предлагает больше возможностей, чем awk, например, изначально понимая шестнадцатеричные и восьмеричные целые числа.

perl -le 'print 3.1e1 * 2'  # -> 62

Я не понимаю, что такое выходной формат Perl по умолчанию, но он выглядит как %.15g. Как и для awk, вы можете использовать printf для выбора желаемого формата вывода:

perl -e 'printf "%.4f\n", 3.1e1 * 2.1234' # -> 65.8254

(c) Использование printf для преобразования научной нотации в десятичные дроби

Если вы просто хотите преобразовать научную нотацию (например, 1.2e-2) в десятичную дробь (например, 0.012), printf '%f' может сделать это для вас. Обратите внимание, что преобразовать одно текстовое представление в другое с помощью арифметики с плавающей запятой, которая подвержена тем же ошибкам округления, что и awk и perl подходит.

printf '%.4f' '1.2e-2' # -> '0.0120'; `.4` specifies 4 decimal digits.

Ответ 4

Вы также можете определить функцию bash, которая вызывает awk (хорошим именем будет знак равенства "=" ):

= ()
{
    local in="$(echo "[email protected]" | sed -e 's/\[/(/g' -e 's/\]/)/g')";
    awk 'BEGIN {print '"$in"'}' < /dev/null
}

Затем вы можете использовать все типы математики с плавающей запятой в оболочке. Обратите внимание, что здесь вместо квадратных скобок используются квадратные скобки, так как последние должны быть защищены от bash кавычками.

> = 1+sin[3.14159] + log[1.5] - atan2[1,2] - 1e5 + 3e-10
0.94182

Или в script, чтобы назначить результат

a=$(= 1+sin[4])
echo $a   # 0.243198

Ответ 5

К счастью, есть printf, который выполняет задание форматирования:

В приведенном выше примере:

printf "%.12f * 2\n" 3.1e1 | bc -l

Или сравнение с плавающей точкой:

n=8.1457413437133669e-02
m=8.1456839223809765e-02

n2=`printf "%.12f" $n`
m2=`printf "%.12f" $m`

if [ $(echo "$n2 > $m2" | bc -l) == 1  ]; then 
   echo "n is bigger"
else
   echo "m is bigger"
fi

Ответ 6

попробуйте это (это найдено в примере для входных данных CFD для обработки с помощью m4:)

T0=4e-5
deltaT=2e-6
m4 <<< "esyscmd(perl -e 'printf (${T0} + ${deltaT})')"

Ответ 7

Попробуйте следующее: (используя bash)

printf "scale=20\n0.17879D-13\n" | sed -e 's/D/*10^/' | bc

или это:

 num="0.17879D-13"; convert="`printf \"scale=20\n$num\n\" | sed -e 's/D/*10^/' | bc`" ; echo $convert
.00000000000001787900
num="1230.17879"; convert="`printf \"scale=20\n$num\n\" | sed -e 's/D/*10^/' | bc`" ; echo $convert
1230.17879

Если у вас есть положительные показатели, вы должны использовать это:

num="0.17879D+13"; convert="`printf \"scale=20\n$num\n\" | sed -e 's/D+/*10^/' -e 's/D/*10^/' | bc`" ; echo $convert
1787900000000.00000

Этот последний обрабатывал бы все числа, которые были бы брошены на него. Вы можете адаптировать "sed", если у вас есть числа с "e" или "E" в качестве показателей.

Вы можете выбрать желаемый масштаб.