100 Languages Speedrun: Episode 38: BC

I like covering languages you already have installed, but don't know about it. So today's episode is about BC - arbitrary precision calculator language.

Its main selling point is in the name - arbitrary precision. Its main downside is also in the name - calculator language - as it's extremely limited.

Using bc

bc expects a file, but we can use it in a few ways. You can run it from shell (-q is quiet mode, otherwise it prints annoying 4 line banner if used this way):

$ bc -q
2^300
20370359763344860862684456884093781610514683936659362506361404493543\
81299763336706183397376
quit

With 2^300 and quit being input. As you can see the output is being automatically formatted to not exceed 70 columns.

If you want to just pass some code on the command line, there's no equivalent to -e "code" most other Unix languages have. You need to echo the code and pipe it into bc:

$ echo '2^100' | bc
1267650600228229401496703205376

Most shells nowadays have a shortcut for this kind of echo. It's up to you if it's more readable:

$ bc <<< '2^100'
1267650600228229401496703205376

You can also use bc from a script, but it needs both -q (to prevent printing the stupid banner), and quit command at the end (otherwise it will continue expecting your input):

#!/usr/bin/bc -q

2^100
quit

This is actually intentional feature, as you can define some functions and whatnot in the file, and then do calculations on them. For example with this file:

#!/usr/bin/bc -q

a = 100
b = 200

We can do this:

$ ./interactive.bc
a+b
300
quit

Powers of 2

The looping is done with while. Any calculation that doesn't get assigned to some variable is interpretted as a print. So this a line means to print a:

#!/usr/bin/bc -q

a = 1
i = 1
while (i <= 32) {
  a
  a = a*2
  i = i+1
}
quit

It works just as expected:

$ ./powers.bc
1
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
2097152
4194304
8388608
16777216
33554432
67108864
134217728
268435456
536870912
1073741824
2147483648

Fibonacci

The Fibonacci sequence is just as you'd expect, the interesting part is printing it:

#!/usr/bin/bc -q

a = 1
b = 1
i = 1
while (i <= 100) {
  if (i >= 90) {
    "fib("
    print(i)
    ") = "
    print(a)
    "
"
  }
  c = a+b
  a = b
  b = c
  i = i+1
}
quit

Which generates:

./fib.bc
fib(90) = 2880067194370816120
fib(91) = 4660046610375530309
fib(92) = 7540113804746346429
fib(93) = 12200160415121876738
fib(94) = 19740274219868223167
fib(95) = 31940434634990099905
fib(96) = 51680708854858323072
fib(97) = 83621143489848422977
fib(98) = 135301852344706746049
fib(99) = 218922995834555169026
fib(100) = 354224848179261915075

Step by step:

  • any calculation without assignment like a prints a followed by a newline
  • print(a) also prints a - but without a newline
  • "string" prints a string
  • if you want to print a newline alone, there's no "\n" - you need to start " on one line, and end on the next.
  • in this case we could just do a instead of print a followed by print newline trick, but I wanted to show the full range of printing options.

FizzBuzz

The first problem we run into is that bc has if but it doesn't have else, so we need to either return from the function or do the second if with reversed condition.

The second problem is that if we call fizzbuzz(i), bc will try to print the result of the function, which is 0, and so we need to assign it to something. bc does not support _ as variable name, so the convention of _ = something() so common in many other languages for throwing out results is not really possible:

#!/usr/bin/bc -q

define fizzbuzz(n) {
  if (n % 15 == 0) {
    "FizzBuzz
"
    return
  }
  if (n % 5 == 0) {
    "Buzz
"
    return
  }
  if (n % 3 == 0) {
    "Fizz
"
    return
  }
  n
}

i = 1
while (i <= 100) {
  unused = fizzbuzz(i)
  i = i + 1
}
quit

Knowing these limitations, we can rewrite it in a nicer way:

#!/usr/bin/bc -q

i = 1
while (i <= 100) {
  t = i % 3
  v = i % 5
  if (t == 0) { "Fizz" }
  if (v == 0) { "Buzz" }
  if (t != 0 && v != 0) { print(i) }
  "
"
  i = i + 1
}
quit

Pythagorean theorem

Just about every language supports arbitrary precision integers, so none of that has been impressive. Let's try something that is a bit less common - arbitrary precision decimals.

By default bc uses scale = 0, that is everything is an integer. We need to set scale to however many decimal places we want:

#!/usr/bin/bc -q

scale = 60
a = read()
b = read()
sqrt(a^2 + b^2)
quit

Which prints the answer to 60 decimal places:

$  ./pythagoras.bc
69
420
425.630121114565856802783307310908277784184864514438295059161980

Should you use BC?

No.

It had a niche back in the olden days when the alternatives were C and Shell. Nowadays most languages have arbitrary sized integers - either by default like Ruby or Python, or with easy one letter suffix like JavaScript's 69n, so there's really no point whatsoever in learning bc if you just need big integers.

With arbitrary precision decimals, it's arguably somewhat useful. All languages have some kind of arbitrary precission decimal libraries, but they're often a bit awkward to use.

Still, I don't think this makes learning bc worth it - for almost every use case, unlimited integers and regular builtin floating point numbers are sufficient, and if you find yourself needing arbitrary precission decimals or arbitrary precission float a lot, you should probably learn proper mathematical language like Julia.

Code

All code examples for the series will be in this repository.

Code for the BC episode is available here.