vitutor reglas operaciones notacion hacer ejercicios convertir como cientifica basicos bash numeric floating-accuracy bc

bash - reglas - ¿Cómo obtener bc para manejar números en notación científica(también conocida como exponencial)?



notacion cientifica vitutor (8)

bc no le gustan los números expresados ​​en notación científica (también conocida como notación exponencial).

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

pero necesito usarlo para manejar algunos registros que se expresan en esta notación. ¿Hay alguna manera de hacer que bc entienda la notación exponencial? Si no, ¿qué puedo hacer para traducirlos a un formato que bc entienda?


Afortunadamente hay printf, que hace el trabajo de formateo:

El ejemplo anterior:

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

O una comparación flotante:

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


Desafortunadamente, bc no soporta notación científica.

Sin embargo, se puede traducir a un formato que bc puede manejar, usando expresiones regulares extendidas según POSIX en sed:

sed -E ''s/([+-]?[0-9.]+)[eE]/+?(-?)([0-9]+)/(/1*10^/2/3)/g'' <<<"$value"

puede reemplazar la "e" (o "e +", si el exponente es positivo) con "* 10 ^", que bc entenderá rápidamente. Esto funciona incluso si el exponente es negativo o si el número se multiplica posteriormente por otra potencia, y permite mantener un registro de dígitos significativos.

Si necesita atenerse a expresiones regulares básicas (BRE), entonces debe usar esto:

sed ''s//([+-]/{0,1/}[0-9]*/./{0,1/}[0-9]/{1,/}/)[eE]+/{0,1/}/(-/{0,1/}/)/([0-9]/{1,/}/)/(/1*10^/2/3)/g'' <<<"$value"

De los comentarios:

  • Una simple coincidencia de pattern bash no podría funcionar (gracias ) ya que no hay manera de hacer coincidir un e + y mantener el - de un e - al mismo tiempo.

  • Una solución perl que funciona correctamente (gracias )

    $ perl -pe ''s/([-/d.]+)e(?:/+|(-))?(/d+)/($1*10^$2$3)/gi'' <<<"$value"

  • Gracias a @jwpat7 y @Paul Tomblin por aclarar aspectos de la sintaxis de sed, así como a @isaac y por mejorar la respuesta.

Editar:

La respuesta cambió un poco con los años. La respuesta anterior es la última iteración del 17 de mayo de 2018. Los intentos anteriores informados aquí fueron una solución en puro bash (por @ormaaj ) y uno en sed (por ), que fallan en al menos algunos casos. Los mantendré aquí solo para dar sentido a los comentarios, que contienen explicaciones mucho más agradables de las complejidades de todo esto que esta respuesta.

value=${value/[eE]+*/*10^} ------> Can not work. value=`echo ${value} | sed -e ''s/[eE]+*///*10//^/''` ------> Fail in some conditions


Permítame tratar de resumir las respuestas existentes, con comentarios sobre cada una de ellas a continuación :

  • (a) Si realmente necesita usar bc para cálculos de precisión arbitraria , como lo hace el OP, use el enfoque inteligente del propio OP , que reformatea textualmente la notación científica a una expresión equivalente que bc entiende.

  • Si potencialmente perder precisión no es una preocupación ,

    • (b) considere usar awk o perl como bc alternativas ; ambos comprenden de forma nativa la notación científica, como se demuestra en jwpat7''s respuesta jwpat7''s para awk.
    • (c) considere usar printf ''%.<precision>f'' para simplemente convertir textualmente a una representación de punto flotante regular (fracciones decimales, sin la e / E ) (una solución propuesta en una publicación eliminada por @ormaaj ).

(a) Reformateo de la notación científica a una equivalente bc expresión

La ventaja de esta solución es que la precisión se conserva : la representación textual se transforma en una representación textual equivalente que bc puede entender, y bc es capaz de realizar cálculos de precisión arbitraria.

Vea la respuesta del OP , cuya forma actualizada ahora es capaz de transformar una expresión completa que contiene múltiples números en notación exponencial en una expresión bc equivalente.

(b) Usar awk o perl lugar de bc como calculadora

Nota: los siguientes enfoques asumen el uso del soporte incorporado para valores de punto flotante de precisión doble en awk y perl . Como es inherente a la aritmética de punto flotante,
"dado un número fijo de bits, la mayoría de los cálculos con números reales producirán cantidades que no se pueden representar exactamente con tantos bits. Por lo tanto, el resultado de un cálculo de punto flotante a menudo debe redondearse para que se ajuste nuevamente a su representación finita. Esto error de redondeo es el rasgo característico del cálculo de punto flotante ". ( http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html )

Dicho esto,

  • GNU awk ofrece la opción de construirse con soporte para aritmética de precisión arbitraria; consulte https://www.gnu.org/software/gawk/manual/html_node/Gawk-and-MPFR.html ; sin embargo, las distribuciones pueden o no incluir ese soporte; verifique el soporte comprobando la salida de gawk --version para GNU MPFR y GNU MP .
    Si el soporte está disponible, debe activarlo con -M ( --bignum ) en una invocación determinada.

  • Perl ofrece soporte decimal opcional de precisión arbitraria a través del paquete Math::BigFloat - vea https://metacpan.org/pod/Math::BigFloat

awk

awk entiende de forma nativa la notación exponencial (científica) decimal.
(En general, solo debe usar la representación decimal , porque las implementaciones de awk difieren con respecto a si admiten literales numéricos con otras bases).

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

Si utiliza la función de print predeterminada, la variable OFMT controla el formato de salida mediante una printf formato printf ; el valor predeterminado (exigido por POSIX) es %.6g , lo que significa 6 dígitos significativos , que incluyen notablemente los dígitos en la parte entera .

Tenga en cuenta que si el número en notación científica se proporciona como entrada (a diferencia de una parte literal del programa awk), debe agregar +0 para forzarlo al formato de salida predeterminado, si se usa solo con la print :

Según la configuración regional y la implementación de awk que utilice, es posible que deba reemplazar el punto decimal ( . ) Por el carácter de base adecuado para la configuración regional, como en una configuración regional alemana; se aplica a BSD awk , mawk y a GNU awk con la opción --posix .

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

La modificación de la variable OFMT cambia el formato de salida predeterminado (para números con partes fraccionarias; los enteros (efectivos) siempre se emiten como tales).
Alternativamente, use la función printf con un formato de salida explícito:

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

Perl

perl también entiende de forma nativa la notación exponencial (científica) decimal.

Nota: Perl, a diferencia de awk, no está disponible de forma predeterminada en todas las plataformas similares a POSIX ; además, no es tan ligero como awk .
Sin embargo, ofrece más funciones que awk, como la comprensión nativa de enteros hexadecimales y octales .

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

No tengo claro cuál es el formato de salida predeterminado de Perl, pero parece ser %.15g . Al igual que con awk, puede usar printf para elegir el formato de salida deseado:

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

(c) Usar printf para convertir la notación científica a fracciones decimales

Si simplemente desea convertir la notación científica (por ejemplo, 1.2e-2 ) en una fracción decimal (por ejemplo, 0.012 ), printf ''%f'' puede hacer eso por usted. Tenga en cuenta que convertirá una representación textual en otra a través de la aritmética de punto flotante , que está sujeta a los mismos errores de redondeo que los enfoques awk y perl .

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


Prueba esto: (usando bash)

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

o esto:

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

Si tienes exponentes positivos debes usar esto:

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

Ese último se encargaría de todos los números arrojados. Puede adaptar el ''sed'' si tiene números con ''e'' o ''E'' como exponentes.

Puedes elegir la escala que quieras.


También puede definir una función bash que llame a awk (un buen nombre sería el signo igual "="):

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

Luego puedes usar todo tipo de matemática de punto flotante en el shell. Tenga en cuenta que los corchetes se utilizan aquí en lugar de los corchetes redondos, ya que estos últimos deberían protegerse del bash entre comillas.

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

O en un script para asignar el resultado.

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


Uno puede usar awk para esto; por ejemplo,

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

produce (a través del formato por defecto awk% .6g) la salida como
12.3457 3.14159 543210000000000000
mientras que los comandos como los dos siguientes producen el resultado que se muestra después de cada uno, dado que el archivo edata contiene datos como se muestra más adelante.

$ 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

Además, con respecto a las soluciones que utilizan sed , probablemente es mejor eliminar el signo más en formas como 45e+3 al mismo tiempo que e , a través de regex [eE]+* , en lugar de en una expresión sed separada. Por ejemplo, en mi máquina Linux con GNU sed versión 4.2.1 y bash versión 4.2.24, comandos
sed ''s/[eE]+*/*10^/g'' <<< ''7.11e-2 + 323e+34''
sed ''s/[eE]+*/*10^/g'' <<< ''7.11e-2 + 323e+34'' | bc -l
producir salida
7.11*10^-2 + 323*10^34
3230000000000000000000000000000000000.07110000000000000000


Versión de tubería de OP aceptada respuesta

$ echo 3.82955e-5 | sed ''s/[eE]+*//*10/^/'' 3.82955*10^-5

Al canalizar la entrada a los OPs, el comando sed dado proporcionaba barras invertidas adicionales como

$ echo 3.82955e-5 | sed ''s/[eE]+*///*10//^/'' 3.82955/*10/^-5


intente esto (lo encontró en un ejemplo para datos de entrada de CFD para procesar con m4 :)

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