Here we explore bc
again, but now more as a language in which to get more involved calculations. Just type bc -lq
and you will get a prompt (the q flag is to avoid the initial message). There we can define any variable, for example:
$ bc -lq a=sqrt(2) b=sqrt(3) a+b 3.14626436994197234232 last 3.14626436994197234232
We don't need to end lines with ;
, although it is always a good idea to do so. We can now write an if
statement:
if(1>0) print "hello\n" hello if(1<0) print "hello\n" else print "bye\n" bye if(1>0) {print "hello 1\n"; print "hello 2\n"} hello 1 hello 2
We now write a for
statement:
for(i=0;i<5;i++) print i," ";print "\n" 0 1 2 3 4 quit $
The built-in mathematical functions are power, square root, sine, cosine, arctangent, natural logarithm, exponential and Bessel function of order n. They are called as ^, sqrt(), s(x), c(x), a(x), l(x), e(x), j(n,x)
. The trigonometric functions work with radians. By default, they operate with 20 decimal places, which is enough for most practical cases.
What if we want to use other trigonometric functions? We can just apply some trigonometric identities:
x=0.1; # tan(x) s(x)/c(x) .10033467208545054505 # arcsin(x) a(x/sqrt(1-x^2)) .10016742116155979633 # arccos(x) a(sqrt(1-x^2)/x) 1.47062890563333682288
An important quantity we may want to use is, of course, π. But we know that tan(π/4)=1
, so that
pi=4*a(1); print pi,"\n" 3.14159265358979323844
A weird finding regarding powers of negative numbers:
$ bc -lq -1^2 1 -(1)^2 1 (-1)^2 1 -(1^2) -1 quit $ python3 -c "print(-1**2)" -1 $ python3 -c "print(-(1)**2)" -1 $ python3 -c "print((-1)**2)" 1 $ python3 -c "print(-(1**2))" -1 $ octave-cli --eval "-1^2" ans = -1 $ octave-cli --eval "-(1)^2" ans = -1 $ octave-cli --eval "(-1)^2" ans = 1 $ octave-cli --eval "-(1^2)" ans = -1
All calculators I know behave like Pyton or Octave in this operation. I find bc
's behaviour quite unsettling here.
We can write all commands to a file, and then execute them all at once. For example:
$ cat snd.bc # standard normal distribution (snd) scale=16 pi=4*a(1); n=100000; dx=10/n; snd=0; for(i=-n;i<n+1;i++) { x=i*dx; snd+=e(-(x^2)/2)*dx; } snd*=1/sqrt(2*pi); print snd,"\n";
We execute the script as
$ time bc -l < snd.bc .9999999999969449 real 0m18.382s user 0m18.377s sys 0m0.004s
We can compare the performance of bc
against the same calculation with NumPy:
$ cat snd.py import numpy as np n=100000 dx=10/n snd=0 for i in range(-n,n+1): x=dx*i; snd+=np.exp(-x**2/2)*dx; snd*=1/np.sqrt(2*np.pi) print(snd)
$ python3 snd.py 0.9999999999998942 real 0m0.629s user 0m0.698s sys 0m0.240s
In the previous case, for n=1000 or even 10000, the differences are not so dramatic. But for n=10000 the time differences become significant. The results are slightly different and I am not sure which one has more accuracy. Python is using here double precision numbers, which means 16 digits, and we have set the bc
scale to 16 as well.
Another potential issue is the lack of fractional exponents, but this is easily overcome:
2^3 8 2^0.5 Runtime warning (func=(main), adr=8): non-zero scale in exponent 1 e(0.5*l(2)) 1.41421356237309504878
We can define functions and store them at ~/.bcrc
. In this repository you can find a nice example. You can summon the functions library with the command:
bc ~/.bcrc -lq
Despite not being the ideal tool for complex and long calculations, this extremely lightweight tool is very capable for most practical situations.