bc
n'aime pas les nombres exprimés en notation scientifique (notation exponentielle).
$ echo "3.1e1*2" | bc -l
(standard_in) 1: parse error
mais je dois l’utiliser pour gérer quelques enregistrements exprimés dans cette notation. Est-il possible d'obtenir que bc
comprenne la notation exponentielle? Sinon, que puis-je faire pour les traduire dans un format que bc
comprendra?
Malheureusement, bc ne supporte pas la notation scientifique.
Cependant, il peut être traduit dans un format que bc peut gérer, en utilisant extended regex selon POSIX dans sed:
sed -E 's/([+-]?[0-9.]+)[eE]\+?(-?)([0-9]+)/(\1*10^\2\3)/g' <<<"$value"
vous pouvez remplacer le "e" (ou "e +", si l'exposant est positif) par "* 10 ^", ce que bc comprendra rapidement. Cela fonctionne même si l'exposant est négatif ou si le nombre est ensuite multiplié par une autre puissance et permet de garder une trace des chiffres significatifs.
Si vous devez vous en tenir à une expression rationnelle de base (BRE), utilisez-le:
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 commentaires:
Une simple correspondance bash pattern ne pourrait pas fonctionner (merci @ mklement0 ) car il n’existait aucun moyen de faire correspondre un e + et de conserver le - de e- en même temps.
Une solution Perl qui fonctionne correctement (merci @ mklement0 )
$ Perl -pe 's/([-\d.]+)e(?:\+|(-))?(\d+)/($1*10^$2$3)/gi' <<<"$value"
Merci à @ jwpat7 et @Paul Tomblin pour avoir clarifié certains aspects de la syntaxe de sed, ainsi que @isaac et @ mklement0 pour avoir amélioré la réponse.
Modifier:
La réponse a changé un peu au fil des ans. La réponse ci-dessus est la dernière itération du 17 mai 2018. Les tentatives précédentes signalées ici étaient une solution en pure bash (par @ormaaj ) et une autre en sed (par @me ), qui échouent à moins certains cas. Je les garderai ici juste pour donner un sens aux commentaires, qui contiennent des explications bien plus jolies sur les subtilités de tout cela que cette réponse.
value=${value/[eE]+*/*10^} ------> Can not work.
value=`echo ${value} | sed -e 's/[eE]+*/\\*10\\^/'` ------> Fail in some conditions
On peut utiliser awk pour cela; par exemple,
awk '{ print +$1, +$2, +$3 }' <<< '12345678e-6 0.0314159e2 54321e+13'
produit (via le format par défaut de awk% .6g) une sortie comme12.3457 3.14159 543210000000000000
Les commandes while, comme les deux suivantes, produisent la sortie affichée après chacune, étant donné que le fichier edata
contient les données comme indiqué plus tard.
$ 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
De même, en ce qui concerne les solutions utilisant sed
, il est probablement préférable de supprimer les formes de signe plus comme 45e+3
en même temps que e
, via regex [eE]+*
, plutôt que dans une expression sed
séparée. Par exemple, sur ma machine Linux avec GNU sed version 4.2.1 et bash version 4.2.24, les commandessed 's/[eE]+*/*10^/g' <<< '7.11e-2 + 323e+34'
sed 's/[eE]+*/*10^/g' <<< '7.11e-2 + 323e+34' | bc -l
produire une sortie7.11*10^-2 + 323*10^34
3230000000000000000000000000000000000.07110000000000000000
Vous pouvez également définir une fonction bash qui appelle awk (un bon nom serait le signe égal "="):
= ()
{
local in="$(echo "$@" | sed -e 's/\[/(/g' -e 's/\]/)/g')";
awk 'BEGIN {print '"$in"'}' < /dev/null
}
Vous pouvez ensuite utiliser tous les types de maths en virgule flottante dans le shell. Notez que les crochets sont utilisés ici au lieu des crochets ronds, car ces derniers devraient être protégés de la bash par des guillemets.
> = 1+sin[3.14159] + log[1.5] - atan2[1,2] - 1e5 + 3e-10
0.94182
Ou dans un script pour assigner le résultat
a=$(= 1+sin[4])
echo $a # 0.243198
Heureusement, il y a printf, qui fait le travail de formatage:
L'exemple ci-dessus:
printf "%.12f * 2\n" 3.1e1 | bc -l
Ou une comparaison de float:
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
Essayez ceci: (en utilisant bash)
printf "scale=20\n0.17879D-13\n" | sed -e 's/D/*10^/' | bc
ou ca:
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 vous avez des exposants positifs, vous devriez utiliser ceci:
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
Ce dernier traiterait tous les chiffres. Vous pouvez adapter le 'sed' si vous avez des nombres avec 'e' ou 'E' comme exposants.
Vous devez choisir l’échelle que vous voulez.
Version de tuyauterie des PO acceptés réponse
$ echo 3.82955e-5 | sed 's/[eE]+*/\*10\^/'
3.82955*10^-5
En transmettant l’entrée aux OP, la commande sed acceptée a généré des barres obliques inverses telles que
$ echo 3.82955e-5 | sed 's/[eE]+*/\\*10\\^/'
3.82955\*10\^-5
essayez ceci (trouvé dans un exemple de données d'entrée CFD à traiter avec m4 :)
T0=4e-5
deltaT=2e-6
m4 <<< "esyscmd(Perl -e 'printf (${T0} + ${deltaT})')"
J'ai réussi à le faire avec un petit bidouillage. Vous pouvez faire quelque chose comme ça -
scientific='4.8844221e+002'
base=$(echo $scientific | cut -d 'e' -f1)
exp=$(($(echo $scientific | cut -d 'e' -f2)*1))
converted=$(bc -l <<< "$base*(10^$exp)")
echo $converted
>> 488.4422100