Fancy Tricks for Changing Numeric Base

In this article, I'm covering something that's a little abstruse: converting numeric bases within shell scripts. There are really four commonly used numeric bases to consider: binary, octal, decimal and hexadecimal. You're used to working in base-10, so 10 = 1 * 10**1 + 0 and 100 = 1 * 10**2 + 0 * 10**1 + 0.

That maps to other numeric bases, so 1010 base-2 or binary is really 1 * 2**3 + 0 * 2**2 + 1 * 2**1 + 0 or 8 + 0 + 2 + 0 = 10 decimal. Octal is the same thing, so 33 base-8 converts to decimal as 3 * 8**1 + 3 = 27.

Hexadecimal presents a different challenge because a base-16 numbering system doesn't fit neatly into our Arabic numerals 0, 1, 2, ... 9. "Hex", as it's known informally, adds A, B, C, D, E and F, so that the decimal value 10 is represented in Hex as "A". That's where the math gets interesting, so 33 base-16 = 3 * 16**1 + 3 = 48 + 3 = 51.

The long, complicated way to create a base conversion utility is therefore to disassemble every value given and apply the translation shown, then have an internal value that's a common base (probably base-10), then have another routine that converts the common base to the desired output base.

There are smarter ways to do this, as I'll discuss, but for now, let's look at the bc command, which supports users specifying both the input and output numeric bases. bc, the binary calculator, is a bit tricky to work with as it's an old-school UNIX command. As I discuss at length in my book Wicked Cool Shell Scripts, the most common way to work with the crude but interactive bc program is to use echo to send it the commands needed, as demonstrated here:


$ echo '333 * 0.35' | bc
116.55

Useful (particularly since expr and $(( )) can't work with floats and decimal values), but where this gets really interesting is with those input and output numeric bases.

Let's say I want to confirm a conversion I listed earlier, by converting 33 hex into decimal. This is easily done:


$ echo 'ibase=16; 33' | bc
51

That's simple. Now, let's do something bigger and more complicated:


$ echo 'ibase=16; FEF33D9' | bc
267334617

ibase is the input numeric base. The output base is specified as obase. And that's it—easy enough!

So let's take the same hex value as input but force the output to octal instead of the default decimal:


$ echo 'ibase=16; obase=8; FEF33D9' | bc
1773631731

Would you rather work in binary? You can do that too:


$ echo 'ibase=16; obase=2; FEF33D9' | bc
1111111011110011001111011001

That's a lot of ones and zeroes, for sure. It makes me think of Interstellar, but that's another article entirely!

______________________

Dave Taylor has been hacking shell scripts for over thirty years. Really. He's the author of the popular "Wicked Cool Shell Scripts" and can be found on Twitter as @DaveTaylor and more generally at www.DaveTaylorOnline.com.