bc
不喜欢用科学记数法表示的数字(又称指数表示法)。
$ echo "3.1e1*2" | bc -l
(standard_in) 1: parse error
但是我需要用它来处理用这种表示法表示的一些记录。有没有办法让bc
理解指数表示法?如果没有,我该怎么做才能将它们转换为bc
会理解的格式?
答案 0 :(得分:26)
不幸的是,bc不支持科学记数法。
但是,可以使用sed中的extended regex as per POSIX将其翻译成bc可以处理的格式:
sed -E 's/([+-]?[0-9.]+)[eE]\+?(-?)([0-9]+)/(\1*10^\2\3)/g' <<<"$value"
你可以用“* 10 ^”替换“e”(或“e +”,如果指数是正数),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匹配无效(感谢@mklement0),因为无法匹配e +并同时保留 - 与e-。
正确运行的perl解决方案(感谢@mklement0)
$ perl -pe 's/([-\d.]+)e(?:\+|(-))?(\d+)/($1*10^$2$3)/gi' <<<"$value"
感谢@jwpat7和@Paul Tomblin澄清了sed语法的各个方面,以及@isaac和@mklement0来改进答案。
< / LI>编辑:
这些年来,答案发生了很大变化。上面的答案是截至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
答案 1 :(得分:14)
让我尝试总结现有答案,对每个答案进行评论:
(a)如果您确实需要使用 bc
进行任意 - 精度计算 - 正如OP所做的那样 - 使用{{3 textually 将科学记数法重新格式化为bc
理解的等效表达式 。
如果可能会失去精确度 不是,,
awk
或perl
作为bc
替代方案;本身都理解科学记数法,如OP's own clever approach回答awk所示。printf '%.<precision>f'
将 文本转换转换为常规浮点表示(小数部分,不含e
/ E
)(由jwpat7's在自删除帖子中提出的解决方案。)bc
表达式 此解决方案的优点是精度得以保留:文本表示转换为bc
可以理解的等效文本表示,并{{ 1}}本身能够进行任意精度计算。
请参阅ormaaj,其更新后的表单现在能够将包含指数表示法中多个数字的整个表达式转换为等效的bc
表达式。
bc
或awk
代替perl
作为计算器注意:以下方法假设使用内置支持bc
和awk
中的双精度浮点值。
正如浮点运算中固有的那样,
&#34;给定任何固定数量的位,大多数带有实数的计算将产生无法使用那么多位精确表示的量。因此,浮点计算的结果通常必须舍入,以便适应其有限表示。这个舍入误差是浮点计算的特征。&#34; (OP's own answer)功能
那就是说,
GNU awk提供选项 构建支持任意精度算术 - 请参阅http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html;但是,发布可能包含或不包含支持 - 通过检查perl
gawk --version
和GNU MPFR
的输出来验证支持。
如果支持 可用,则必须在给定调用中使用GNU MP
(-M
)激活它。
Perl通过--bignum
包提供可选任意精度小数支持 - 请参阅https://www.gnu.org/software/gawk/manual/html_node/Gawk-and-MPFR.html
<强> AWK 强>
Math::BigFloat
本身理解十进制指数(科学)表示法。
(通常应该只使用 decimal 表示,因为awk
实现在它们是否支持带有其他基数的数字文字方面有所不同。)
awk
如果使用默认的awk 'BEGIN { print 3.1e1 * 2 }' # -> 62
函数,print
变量将通过OFMT
格式字符串控制输出格式; (POSIX规定)默认值为printf
,表示6 有效数字,其中包括整数部分中的数字 。
请注意,如果科学计数法中的数字作为输入提供(与awk程序的文字部分相对),则必须添加%.6g
以强制它为默认输出格式,如果使用本身与+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
<强>的Perl 强>
awk 'BEGIN { printf "%.4f", 3.1e1 * 2.1234 }' # -> 65.8254
本身也理解十进制指数(科学)表示法。
注意:Perl与awk不同,默认情况下并非在所有类似POSIX的平台上都可用;此外,它的强度不如awk 轻 但是,它提供了比awk更多的功能,例如本机地理解十六进制和八进制整数。
perl
我不清楚Perl的默认输出格式是什么,但似乎是perl -le 'print 3.1e1 * 2' # -> 62
。
与awk一样,您可以使用%.15g
选择所需的输出格式:
printf
perl -e 'printf "%.4f\n", 3.1e1 * 2.1234' # -> 65.8254
将科学记数法转换为小数分数如果您只是想将科学记数法(例如printf
)转换为小数(例如1.2e-2
),0.012
可以为您做到这一点。
请注意,您通过浮点运算将文本表示转换为另一个 ,这是主题与printf '%f'
和awk
接近相同的舍入错误。
perl
答案 2 :(得分:11)
可以使用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
表达式。例如,在我的带有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
答案 3 :(得分:6)
你也可以定义一个调用awk的bash函数(一个好名字就是等号“=”):
= ()
{
local in="$(echo "$@" | sed -e 's/\[/(/g' -e 's/\]/)/g')";
awk 'BEGIN {print '"$in"'}' < /dev/null
}
然后你可以在shell中使用所有类型的浮点数学。请注意,此处使用方括号而不是圆括号,因为后者必须通过引号来保护bash。
> = 1+sin[3.14159] + log[1.5] - atan2[1,2] - 1e5 + 3e-10
0.94182
或者在脚本中分配结果
a=$(= 1+sin[4])
echo $a # 0.243198
答案 4 :(得分:2)
幸运的是有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
答案 5 :(得分:1)
OP的管道版本接受了答案
$ echo 3.82955e-5 | sed 's/[eE]+*/\*10\^/'
3.82955*10^-5
将输入管道输入OPs接受的sed命令给出了额外的反斜杠,如
$ echo 3.82955e-5 | sed 's/[eE]+*/\\*10\\^/'
3.82955\*10\^-5
答案 6 :(得分:0)
试试这个(在一个CFD输入数据的例子中找到这个,用m4进行处理:)
T0=4e-5
deltaT=2e-6
m4 <<< "esyscmd(perl -e 'printf (${T0} + ${deltaT})')"
答案 7 :(得分:0)
试试这个:(使用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
最后一个会处理每个被抛出的数字。如果您的数字为'e'或'E'作为指数,您可以调整'sed'。
您可以选择所需的比例。
答案 8 :(得分:0)
我设法做到了一点点技巧。您可以执行以下操作-
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