web-dev-qa-db-ja.com

科学的(指数)表記で数値を処理するためにbcを取得する方法は?

bcは、科学表記法(別名指数表記法)で表現された数値を好みません。

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

しかし、この表記で表現されるいくつかのレコードを処理するために使用する必要があります。 bcを取得して指数表記を理解する方法はありますか?そうでない場合、bcが理解できる形式に変換するにはどうすればよいですか?

35

残念ながら、bcは科学表記法をサポートしていません。

ただし、sedで POSIXによる拡張正規表現 を使用して、bcが処理できる形式に変換できます。

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

「e」(または指数が正の場合は「e +」)を「* 10 ^」に置き換えることができます。これは、bcがすぐに理解します。これは、指数が負の場合、または数値にその後別の累乗を掛けた場合でも機能し、有効数字を追跡できます。

基本正規表現(BRE)に固執する必要がある場合は、これを使用する必要があります。

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"

コメントから:

  • 単純なbash pattern マッチは機能しませんでした( @ mklement に感謝します)。e+にマッチし、同時にe-から-を保持する方法がないためです。

  • 正しく動作するPerlソリューション(ありがとう @ mklement

    $ Perl -pe 's/([-\d.]+)e(?:\+|(-))?(\d+)/($1*10^$2$3)/gi' <<<"$value"
    
  • @ jwpat7@ Paul Tomblin に感謝します。sedの構文の側面を明確にしてくれました。そして @ isaac@ mklement =答えを改善するため。

編集:

答えは長年にわたってかなり変化しました。上記の答えは、2018年5月17日の最新の反復です。ここで報告された以前の試みは、純粋なbash( @ ormaaj による)とsed( @ me による) )、少なくともいくつかの場合に失敗します。コメントの意味を理解するためだけにここにそれらを保持します。コメントには、この答えよりも複雑なすべてのより良い説明が含まれています。

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

これにはawkを使用できます。例えば、

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

(awkのデフォルト形式%.6gを介して)次のような出力を生成します
12.3457 3.14159 543210000000000000
whileコマンドは、ファイルedataに後述のデータが含まれている場合、それぞれの後に次の2つのような出力を生成します。

$ 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を使用するソリューションに関しては、eと同時に、正規表現45e+3を介して、[eE]+*などのフォームのプラス記号を削除することをお勧めします。個別のsed式ではなく。たとえば、GNU sedバージョン4.2.1およびbashバージョン4.2.24のLinuxマシンでは、コマンド
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

11

また、awkを呼び出すbash関数を定義することもできます(適切な名前は等号 "="です):

= ()
{
    local in="$(echo "$@" | 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

または、結果を割り当てるスクリプトで

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

幸いなことに、フォーマット作業を行う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
2
Fridtjof Stein

OPの配管バージョンが回答を受け入れました

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

入力をOPに受け入れられたsedコマンドにパイプすると、

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

これを試してください:(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

最後の1つは、投げられたすべての数字を処理します。 「e」または「E」を指数とする数値がある場合は、「sed」を調整できます。

必要なスケールを選択できます。

0
cpu

これを試してください(m4で処理するためのCFD入力データの例で見つけました:)

T0=4e-5
deltaT=2e-6
m4 <<< "esyscmd(Perl -e 'printf (${T0} + ${deltaT})')"
0
Ma-tri-x

少しハックしてやることができました。このようなことができます-

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
0
markroxor