Advertisement

Design Design Ideas

Convert binary number to any base

Advertisement

Conversion of binary to decimal numbers is often needed in firmware. And it’s done easily enough if multiplication and division by ten are acceptable. However, these operations, especially division, may be too resource-hungry for a low cost microcontroller (MCU).

I was therefore pleased to learn about the double dabble algorithm, which converts binary to decimal efficiently; it went into my bag of tricks immediately. Still, I want to tailor it to my needs – to work with any base. To do that, I needed to know how it actually works.

The double dabble belongs to the second of two groups of algorithms (shift-adjust and adjust-shift). Both can be used to convert binary numbers to other formats. Among them, conversion to the time or angle format of HMS is especially useful. Additionally, there have been ternary (base-3)-related designs & discussions on EDN recently to which this Design Idea is applicable.

Advertisement

The conversion begins with a binary number:

Advertisement

   A = 2N –1 aN –1 + … + 2n an + … + 2 a 1 + a 0                    (1)

where a 's are bits. N bits are needed to store the number A . The a 's are either 0 or 1. An example number is 110110112 .

The number A is defined by a sum:

   A = BM pM + … + Bm pm + … + B 1 p 1 + p0                      (2)

The constants Bm depend on the numerical system. In decimal, these are products of base constants b as follows in (3). In the definition (2), the p 's correspond to decimal places.

b 0 = 10                          B 1 = b 0 = 10
b 1 = 10                          B 2 = b 1 B 1 = 100

bm –1 = 10                      Bm = bm –1 Bm –1 = 10m                      (3)

The decimal system is not the only interesting system. For example, time, in base 60. Now, p 0 , p 1 , and p 2 are seconds, minutes, and hours respectively.

b 0 = 60                          B 1 = b 0 = 60
b 1 = 60                          B 2 = b 1 B 1 = 3600                             (4)

Except on mechanical clock dials, time is displayed by Arabic numerals that end at 9, which is far less than 59. Since it is not necessary that all b 's in the (2) be equal, the solution is simple. To write time with Arabic numerals, the next numerical system (5) is used. Places p 0 and p 1 together are seconds in the decimal system, p 2 and p 3 are minutes, and p 4 shows hours.

b 0 = 10                          B 1 = b 0 = 10
b 1 = 6                            B 2 = b 1 B 1 = 60
b 2 = 10                          B 3 = b 2 B 2 = 600
b 3 = 6                            B 4 = b 3 B 3 = 3600                              (5)

Another system’s constants could be b 0  = 12 and b 1  = 3; A is length in inches, while p 2 , p 1 , and p 0 are yards, feet, and inches respectively.

Later in the text, a ternary system will be discussed. It has the following base constants:

b 0 = 3                            B 1 = b 0 = 3
b 1 = 3                            B 2 = b 1 B 1 = 9

bm –1 = 3                        Bm = bm –1 Bm –1 = 3m                         (6)

When numerical systems are discussed, then the places pm are integers, which are limited to the interval

   0 ≤ pm  bm                                                                          (7)

pm must be less than the corresponding base constant bm . For decimal, pm is an integer: 0 ≤ pm   holds true also when places pm take values beyond the interval (7)! It is not even necessary that p 's are integers, but we should keep to the positive integers because, well, MCUs.

Let’s assume that A is a binary number 0xFF and that base constants Bm belong to the decimal system (3). The equation (2) holds even when p 0  = 255, p 1  = 0, and p 2  = 0. This is an extreme. It holds also when p 0  = 25, p 1  = 13, and p 2  = 1. But normally, the values are p 0  = 5, p 1  = 5, and p 2  = 2. That is, 0xFF = 255.

In code or hardware for conversion between bases, Rm bits of memory are allocated for each place pm . Usually, nibbles (Rm  = 4) or bytes (Rm  = 8) are used. The allocated memory is big enough when 2Rm    bm holds. Still, the allocated chunk of memory should be a bit larger to provide some space for bit carrying. Thus, the place pm is an integer that is limited to the interval 0 ≤ pm  Rm .

Additional space in pm eases bit carrying from pm  to pm +1 . What is this bit carrying? It is executed when pm +1 is incremented by one (a bit) and pm is decremented by the base constant bm . Decrementing is possible as long as pm  ≥ bm . Let us have a binary number A  = 0xDB and base constants bm of the decimal system (3). Then, let the places be p 2  = 2, p 1  = 0, and p 0  = 19. We can see that the equation (2) holds. We can also see that at least five bits are necessary to store p 0 . To carry from p 0 to p 1 , the place p 1 is incremented by 1 and p 0 is decremented by b 0  = 10. The p 0 and p 1 are adjusted. Now, the places are p 2  = 2, p 1  = 1, and p 0  = 9. That is, 0xDB = 219. During the bit carrying, the equation (2) is always valid.

As said, the bit carrying consists of two steps. In one step, the place pm +1 is incremented. In the other step, the base constant bm is subtracted from pm . The two steps can be reduced into one by combining the two places pm +1 and pm into one number 2Rm  pm +1  + p m . This number already exists if p 's are allocated successively in the MCU's memory. That is, two successive bytes form one unsigned int. Then, a carry constant 2Rm  – b m is added to this new number. The effect can be seen in (8). The higher byte is incremented by one and the lower byte is decremented by bm .

   pm  ≥ bm , (2Rm pm +1 + p m ) + (2Rm b m ) = 2Rm (pm +1 + 1) + (pm b m )                    (8)

The order of adjusting places pm in (2) plays no role. Either pair of places p 0 , p 1 or pM , pM –1 , or any other pair, can be adjusted first. It is because equation (2) holds after any bit carrying. Despite that, it is beneficial that adjustment is executed from p 0 upwards. This way, no pm needs to be adjusted twice. By repeating adjustments, values of individual p 's are made smaller until the condition (7) is valid for all pm . The places pm are considered adjusted then.

Conversion of binary to some other system is possible by bit carrying only. The initial binary number A is moved into p 0 , which must be large enough. Then, bit carrying is repeated until the condition (7) is fulfilled for all p 's. At this point, the number A is converted. Although this method works, it is inefficient. To speed up the conversion, left shifting, which has the same effect as multiplying by two, is introduced. Then, shifting and bit carrying are executed sequentially. The bit carrying is executed after each shift to keep the needed size of p 's as small as possible. At the same time, condition (7) is fulfilled. Because of the sequence, this is called a shift-adjust algorithm.

The algorithm runs in iterations. In the initializing iteration i  = 0, all p 's are cleared. Because of that, equation (9) is zero. Of course, condition (7) holds for all pm , too.

   BM pM (i = 0) + … + Bm pm (i = 0) + … + B 1 p 1 (i = 0) + p 0 (i = 0) = 0            (9)

Now, the equation is multiplied by two. At the same time, bit aN –1 , which is the MSb of A in (1), is added to p 0 . In assembler, this is done by left rotate through carry on A and p 0 . Memory chunks of other p 's should be large enough so that p 's do not overflow during the shifting. They are shifted individually.

   2(BM pM (i = 0) + … + Bm pm (i = 0) + … + B 1 p 1 (i = 0) + p 0 (i = 0)) + aN –1 = aN –1              (10)

After shifting, (10), the bit carries are repeated until the conditions in (7) are met for all m . Adjusting is not yet needed after the first shift. But to keep to the shift-adjust algorithm, adjusting of p 's by bit carrying takes place after each shift. After the first iteration is completed, (7) holds for all m . An equation (11) is obtained, which is an input to the second iteration.

   BM pM (i = 1) + … + Bm pm (i = 1) + … + B 1 p 1 (i = 1) + p 0 (i = 1) = aN –1                 (11)

Now, shifting and adjusting take place again.

   BM pM (i = 2) + … + Bm pm (i = 2) + … + B 1 p 1 (i = 2) + p 0 (i = 2) = 2aN –1 + aN –2          (12)

The sequence is repeated N -times.

   BM pM (i = N ) + … + Bm pm (i = N ) + … + B 1 p 1 (i = N ) + p 0 (i = N ) = 2N –1 aN –1 + … + 2 a 1 + a 0      (13)

After the last bit carrying is over, binary number A is on the right side of (13). On the left side, A is converted into numerals of the desired numerical system. These numerals are stored in the p 's.

Examples

In Example 1 , a binary number A  = 0xDB is converted into decimal, using the structure p 2 .p 1 .p 0 :A . For each p , we allocate one byte (it is convenient to allocate bytes in 8-bit MCUs).

As we can see in shift1 , the MSb from binary number A is shifted into p 0 . After this shift, no bit carrying is needed in adjust1 because conditions (7) for all p 's are already fulfilled. For the first time, bit carrying is executed in the adjust4 . There, a carry constant, 28  – 10, is added to a two-byte structure 0x000D to get 0x0103. This way, the bit carrying or adjusting is done on places p 0 to p 1 as explained in (8). After adjust8 , there are decimal digits in the p 's.

Example 1  Converting 0xDB to 219 by the shift-adjust algorithm.

00.00.00:DB

shift1

00.00.01:B6

adjust1

00.00.01:B6

shift2

00.00.03:6C

adjust2

00.00.03:6C

shift3

00.00.06:D8

adjust3

00.00.06:D8

shift4

00.00.0D:B0

adjust4

00.01.03:B0

shift5

00.02.07:60

adjust5

00.02.07:60

shift6

00.04.0E:C0

adjust6

00.05.04:C0

shift7

00.0A.09:80

adjust7

01.00.09:80

shift8

02.00.13:00

adjust8

02.01.09:00

We now have a procedure for converting binary numbers to other bases, but it can be made even more code-efficient if we switch shifting and adjusting , which we’ll call an adjust-shift algorithm. In each iteration, all necessary bit carrying is executed first, then positions pm are shifted. After both steps are over, the condition (7) must be fulfilled. Since the last step in an iteration is shifting, pm 's must be properly adjusted before that. Therefore, the bit carry must be executed if pm (i ) ≥ bm /2. In this case, the carry constant (2Rm  – b m )/2 is added to pm , as in (14).

   pm  ≥ bm /2, p m + (2Rm b m )/2 = 2Rm –1 + (2 p mb m )/2                        (14)

At the beginning of each iteration of the adjust-shift algorithm, all places pm comply to (7). Because of that, the (2 p m  – b m )/2 from (14) is always less than b m /2. Furthermore, the b m /2 is less than 2Rm –1 . Consequently, the pm does not overflow when the carry constant (2Rm  – b m )/2 is added to it. That is, the right side of (14) is always less than 2Rm . At the same time, adding the carry constant always sets an MSb, as we can see in (14). This MSb increments pm +1 during the following shifting phase of the iteration. The MSb from pm must be carried to the LSb of pm +1 . Because of that, places of pm in adjust-shift algorithms must be shifted as one big number.

Let's highlight the differences between the shift-adjust and adjust-shift algorithms. In the shift-adjust algorithm, two successive pm 's take place in each bit carrying. On the other hand, pm 's are adjusted individually in adjust-shift algorithm. Shifting is a different story. In the shift-adjust algorithm, each pm is shifted individually. An exception is p 0 , which is shifted together with A . On the other hand, all p 's and A are shifted as one big binary number in adjust-shift.

The carry constant (2Rm   – b m )/2 is the reason why the adjust-shift algorithm is suitable for even bases only. If the bm is odd as in the ternary system, the carry constant would not be integer anymore.

The double dabble algorithm is an adjust-shift algorithm. It converts from binary to decimal (bm  = 10) and its places p are stored in nibbles (Rm  = 4). Therefore, bit carry must be executed when pm  ≥ 5 and the carry constant is (2Rm b m )/2 = 3. That is why the double dabble is called the add 3 algorithm sometimes. In Example 2 , the binary number 0xDB is converted to decimal by the double dabble algorithm.

Example 2  Converting 0xDB to 219 by the double dabble algorithm.

0.0.0:DB

adjust1

0.0.0:DB

shift1

0.0.1:B6

adjust2

0.0.1:B6

3 comments on “Convert binary number to any base

  1. aerio
    July 6, 2018

    “Double Dabble – Computerphilenhttps://www.youtube.com/watch?v=eXIfZ1yKFlAn”

  2. CameronRobertson
    July 16, 2018

    “I'm feeling very glad that I don't have to deal with such figures and numbers in my daily operations at the self storage facility. Honestly speaking there is enough moving parts in the business as it is without having to throw in this bunch of calculation

  3. cmagno
    January 23, 2022

    This method proves also to be extremely efficient in converting very large numbers to any base. As I still haven’t been able to figure out how to do it, I would like to ask if the shift-adjust technique could be generalized to fractional numbers.
    Thanks in advance.

Leave a Reply