Please see the disclaimer.

Introduction

After more than a year of working on an implementation of dc and POSIX bc, I have probably become one of few people in the world who can be considered bc experts. Two others are Philip A. Nelson, the author of the GNU bc, and Carl W. (last name unknown), aka Phodd, whose bc libraries are second-to-none.

For anyone who thinks bc is not very powerful, look at Carl’s work; the amount of things he did is insane!

If you try to use his libraries with my bc, you will get an error when loading the file with abs() because my bc has abs() built-in. Just remove his implementation of abs().

Because of my work, I have learned a few tricks to get bc and dc to do what I need. Here are some of them.

This is a living document. I will keep adding to it as time goes on.

These tips and tricks assume you are using my bc and dc built with the PRNG implemented on the rand branch.

Update (27 Feb 2022): The rand branch has been merged for a long time; you can use the latest version of bc and dc instead.

bc

Tips

Use Carl’s libraries.

Seriously. Just use them. They are extensive, well done, and have a lot of care put into them, even though Carl claims otherwise.

Use BC_ENV_ARGS

My bc accepts an environment variable named BC_ENV_ARGS. This is meant to be set to the arguments you would pass to bc on every invocation. For example, users almost always want bc to load the built-in library, which requires the -l option, so they should set the following:

export BC_ENV_ARGS="-l"

in their .bashrc or equivalent.

My BC_ENV_ARGS are as follows:

export BC_ENV_ARGS="-lg"

The -g (--global-stacks) option is there because I prefer being able to set a global (ibase, obase, scale, or seed) in a function and not have to set it back to the original value before returning from the function.

This leads me to:

Use the -g Option

Seriously, it makes writing bc code much easier. Otherwise, bc will “misbehave,” or in other words, it will do what you do not expect it to do. Global variables will end up with seemingly random values, and it will be a pain to debug.

This is the number one thing I think POSIX should adopt from my bc.

Tricks

Generating an n-bit Random Number

To generate an n-bit unsigned random number, use the following:

irand(2^n)

To generate an n-bit signed random number, use the following:

srand(irand(2^(n-1)))

Calculate the Number of Required Bytes

Ever wondered how many unsigned bytes are needed to hold a certain integer x? Use the following:

ubytes(x)

For signed, two’s-complement bytes, use the following:

sbytes(x)

To find the number of bits, just multiply the result(s) from above by 8.

dc

Tips

Use DC_ENV_ARGS

Like bc, dc has recognizes an environment variable called DC_ENV_ARGS. I don’t use dc, but if I did, my DC_ENV_ARGS would be set to:

export DC_ENV_ARGS="-x"

This leads me to:

Use the -x Option

dc has an option called -x (--extended-register) that allows dc to use (almost) arbitrary variable names (registers) by using extended register mode.

The original dc could only use one-letter variable names, but most dc implementations have ways of extending this, though there are various ways of doing so. For example, the BSD dc allows two-character names.

In my not-so-humble opinion, my dc has done it the best because variable names can be as arbitrary as bc’s variable names. The only thing that you must do is put a space in-between the register command (e.g., l, s, L, S, and logical comparisons) and the register name. Then, dc will parse the next bit as a variable name.

Learn How to Use Strings

In dc, strings are enclosed in square brackets:

[This is a string!]

In my dc, brackets can be escaped with backslash:

[This is a string with brackets \[\].]

But that is the easy part. The weirdest thing about dc is that it doesn’t just use strings for printing messages: it can also execute them.

1[plxx]dsxx

That code will go into an infinite loop printing the number 1. Here is how it works:

  1. The number 1 is pushed onto the stack.
  2. The string [plxx] is pushed onto the stack.
  3. d duplicates the string on the stack and pushes the duplicate onto the top of the stack.
  4. sx pops the copy off of the top of the stack and stores it in the register x.
  5. x pops the top item off of the stack, which was the original string, and executes it.
  6. Since the string was popped off of the stack, the top of the stack is 1, and p prints it without removing it from the stack.
  7. lx copies the contents of register x (which was the duplicated string) and pushes the copy onto the stack.
  8. x pops the top item (the copy of register x), and executes it.
  9. dc goes back to step 6.

With the capabilities that my dc provides, you can use string execution as a stand-in for loops, if statements, and function calls. In the right hands, dc is just as powerful as bc.

Tricks

Create an Infinite Loop

To create an infinite loop in dc, just use the following formula:

  1. Decide what register you will store the string in. For the example below, I have chosen x.
  2. Create a string.
  3. Put l<register>x at the end of the string, where <register> is replaced by the register you chose in step 1.
  4. Put ds<register>x, where <register> is also replaced as above, outside the string.
[<code> lxx]dsxx

You can quit an infinite loop with the Q command.

Create a while Loop

Creating a while loop in dc is a little more involved:

  1. Decide what register you will store the string in. For the example below, I have chosen x again.
  2. Figure out what condition the while loop will need.
  3. Figure out how to implement the condition with boolean arithmetic commands and the conditional execution commands
  4. Put the code for the condition at the end of the string, with the appropriate conditional execution command.
  5. Use that same code to start executing the string or not.
[<code> <condition_code>x]sx <condition_code>x

Create a for Loop

Creating a for loop is just like creating a while loop, except that you have to add a counter:

  1. Decide what register you will store the string in. For the example below, I have chosen x again.
  2. Create a counter register and store the starting counter value in it.
  3. Store the string in the register.
  4. Put a condition at the end of the string load the counter, compare it to the end value, and execute again if the counter is too small.
  5. Put ds<register>x, where <register> is replaced with the register that has the string, after the string.
0si [<code> 10 li <x]dsxx

Create an if Statement

You can also use conditional execution commands to create if statements:

<code> [<if_statement_body_code>]sx =x

The above example uses the =x command to conditionally execute the string in register x.

You can even add an else statement using this dc’s support for them:

<code> [<if_body_code>]sx [<else_body_code>]sy =xey

Conclusion

There isn’t really any point to this post other than to demonstrate that you can use bc and dc for anything a desktop calculator can do. I hope you find these tips and tricks useful!

Also, feel free to send me more.