Bash 如何让bc以科学(又称指数)表示法处理数字?

Bash 如何让bc以科学(又称指数)表示法处理数字?,bash,numeric,floating-accuracy,bc,Bash,Numeric,Floating Accuracy,Bc,bc不喜欢用科学记数法(又称指数记数法)表示的数字 但我需要用它来处理一些用这种符号表示的记录。有没有办法让bc理解指数符号?如果没有,我该怎么做才能将它们转换成bc可以理解的格式?不幸的是,bc不支持科学符号 $ echo "3.1e1*2" | bc -l (standard_in) 1: parse error 但是,它可以转换为bc可以处理的格式,在sed中使用: 可以使用awk来实现这一点;比如说, awk '{ print +$1, +$2, +$3 }' <<<

bc
不喜欢用科学记数法(又称指数记数法)表示的数字


但我需要用它来处理一些用这种符号表示的记录。有没有办法让
bc
理解指数符号?如果没有,我该怎么做才能将它们转换成
bc
可以理解的格式?

不幸的是,bc不支持科学符号

$ echo "3.1e1*2" | bc -l
(standard_in) 1: parse error
但是,它可以转换为bc可以处理的格式,在sed中使用:


可以使用awk来实现这一点;比如说,

awk '{ print +$1, +$2, +$3 }' <<< '12345678e-6 0.0314159e2 54321e+13'
此外,关于使用
sed
的解决方案,最好通过regex
[eE]+*
45e+3
等表单中删除加号,而不是在单独的
sed
表达式中同时删除
e
。例如,在我的linux机器上,GNU-sed版本为4.2.1,bash版本为4.2.24,命令

sed's/[eE]+*/*10^/g'您还可以定义一个调用awk的bash函数(一个好名字应该是等号“=”):

或在脚本中指定结果

a=$(= 1+sin[4])
echo $a   # 0.243198
尝试此方法(在使用m4处理CFD输入数据的示例中发现此方法:)

T0=4e-5
deltaT=2e-6
m4试试这个:(使用bash)

或者这个:

 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
最后一个可以处理所有的数字。如果有以“e”或“e”为指数的数字,则可以调整“sed”


您可以选择您想要的量表。

让我尝试总结现有答案,并在下面的每个答案上添加注释

  • (a) 如果您确实需要使用
    bc
    进行任意精度计算
    (如OP所做的),请使用,它将科学符号以文本形式重新格式化为
    bc
    理解的等效表达式

  • 如果不存在可能丢失精度的问题

    • (b)考虑使用<强>代码> AWK 或<代码> Perl >为<代码> BC/<代码>备选< /强>;正如awk的答案所示,两者都天生理解科学符号
    • (c)考虑使用<强>代码> Prtff '%.f′/代码> <强> >简单地>强>文本转换为规则浮点表示(小数,不含<代码> e <代码> <代码> e <代码> < <强> >(由已删除的帖子提出的解决方案)。< /LI>

(a) 将科学记数法重新格式化为等效的
bc
表达式 此解决方案的优点是保留了精度:将文本表示转换为
bc
可以理解的等效文本表示,并且
bc
本身能够进行任意精度计算

请参见,其更新形式现在能够将包含多个指数表示法数字的完整表达式转换为等效的
bc
表达式


(b) 使用
awk
perl
而不是
bc
作为计算器 注意:以下方法假设在
awk
perl
中使用内置的对双精度浮点值的支持。 正如浮点运算固有的那样,
“给定任何固定的位数,大多数实数计算将产生无法用那么多位数精确表示的量。因此,浮点计算的结果必须经常舍入以适应其有限表示。这种舍入误差是f的特征浮点数计算。”()

也就是说

  • GNU awk提供了支持任意精度算术的选项-请参阅;但是,发行版可能包括也可能不包括该支持—通过检查
    gawk--version
    gnumpfr
    gnump
    的输出来验证支持 如果支持可用,则必须在给定调用中使用
    -M
    --bignum
    )激活它

  • Perl通过
    Math::BigFloat
    包提供可选的任意精度十进制支持-请参阅

awk

awk
天生理解十进制指数(科学)记数法。

(通常只应使用十进制表示法,因为
awk
实现在是否支持带其他基的数字文本方面有所不同。)

如果使用默认的
print
功能,则
OFMT
变量通过
printf
格式字符串控制输出格式;(POSIX强制)默认值为
%.6g
,表示6个有效数字,其中包括整数部分的数字

perl -le 'print 3.1e1 * 2'  # -> 62
printf '%.4f' '1.2e-2' # -> '0.0120'; `.4` specifies 4 decimal digits.
请注意,如果科学记数法中的数字作为输入提供(与awk程序的文字部分相反),则必须添加
+0
,以强制其为默认输出格式(如果与
打印一起使用):

根据您的语言环境和所使用的
awk
实现,您可能需要将小数点(
)替换为适合语言环境的基数字符,例如德语语言环境中的
;适用于BSD
awk
mawk
,以及使用
--posix
选项的GNU
awk

Perl

perl
天生就理解十进制指数(科学)记数法。

注意:Perl与awk不同,默认情况下并非在所有类似POSIX的平台上都可用;此外,它不像awk那样轻量级
但是,它提供了比awk更多的功能,例如本机理解十六进制和八进制整数

perl -le 'print 3.1e1 * 2'  # -> 62
printf '%.4f' '1.2e-2' # -> '0.0120'; `.4` specifies 4 decimal digits.
a=$(= 1+sin[4])
echo $a   # 0.243198
T0=4e-5
deltaT=2e-6
m4 <<< "esyscmd(perl -e 'printf (${T0} + ${deltaT})')"
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
awk 'BEGIN { print 3.1e1 * 2 }'  # -> 62
awk '{ print $1+0 }' <<<'3.1e1' # -> 31; without `+0`, output would be the same as input
awk 'BEGIN { printf "%.4f", 3.1e1 * 2.1234 }' # -> 65.8254
perl -le 'print 3.1e1 * 2'  # -> 62
perl -e 'printf "%.4f\n", 3.1e1 * 2.1234' # -> 65.8254
printf '%.4f' '1.2e-2' # -> '0.0120'; `.4` specifies 4 decimal digits.
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
$ echo 3.82955e-5 | sed 's/[eE]+*/\*10\^/'
3.82955*10^-5
$ echo 3.82955e-5 | sed 's/[eE]+*/\\*10\\^/'
3.82955\*10\^-5
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