If I have two numbers in packed BCD format and want to add them, is it a good approach to add them like this: convert both numbers to integers, perform a normal integer addition, then convert the result back to BCD?
-
Not sure why there are close votes, the question seems fine to me. There are ways to add multi-digit BCD operands word-wise using bit operations etc. Generally such approaches tend to be faster than using the dual-conversion route. How many BCD digits do your operands comprise. I may have some C code to demonstrate the conversion-less approach. – njuffa Apr 26 '15 at 09:09
-
Sure. There are lots of possibilities (hence the close votes), but if you are confident you can make it work, then why not? – Jongware Apr 26 '15 at 09:09
-
@njuffa this is packed BCD, 2-digit number(which is packed in one 8-bit `uint8_t`. My general problem is, when i don't do double conversion, how can i check if my first or second number is beyond nine, and what if i want add two numbers and after addition they are beyond 99, or first number is beyond 9, or second is beyond 9, how can i check it? I know that my question is messy, but I don't know how to describe it. – bingo157 Apr 26 '15 at 09:26
-
You need to find the "nibble-carry". Generally, the trick is to add 6 to each nibble after adding the packed BCD operands with a regular integer add, this causes a carry to the next nibble if the nibble sum exceeded 9. I am working on example code right now, but it'll be a while before it is done and tested. – njuffa Apr 26 '15 at 09:31
-
Ok, thanks a lot, I have a lot problem with this hen I'm not using double conversion – bingo157 Apr 26 '15 at 09:37
1 Answers
The C99 code below adds packed BCD operands with eight BCD digits stored in a uint32_t
. This code can easily be extended to wider BCD operands by choosing uint64_t
to process 16 BCD digits. Since this approach relies on bit-parallel processing it may not be efficient for narrow packed BCD operands.
In a packed BCD format, each BCD digit occupies one nibble (4-bit group) of an unsigned integer operand. If nibble-wise addition results in a sum > 9, we want a carry into the next higher nibble. If we use regular integer addition to add two packed BCD operands, the desired nibble carries will not occur when the nibble sum is > 9, but < 16. To remedy this, we can add an additional 6 to each nibble sum.
We can find the nibble carries as follows: The bit-wise sum of two integers x
, y
is x ^ y
. At any bit position that has a carry-in from the next lower bit position during regular integer addition, the bits in x ^ y
and x + y
will differ. So we can find bits with carry-in as (x ^ y) ^ (x + y)
. We are interested in bits 4, 8, ..., 32 for the carry-in, which are the carry-outs from bits 3, 7, ..., 31.
There is a slight problem if there is a carry-out from bit 31 to bit 32 since the uint32_t
operands only hold 32 bits. We can detect this if we find that the sum of two unsigned integers is smaller than either of the addends. The three operations handling the carry-out from bit 31 can be omitted when operating on seven-digit operands instead of eight-digit operands.
/* Add two packed BCD operands, where each uint32_t holds 8 BCD digits */
uint32_t bcd_add (uint32_t x, uint32_t y)
{
uint32_t t0, t1;
t0 = x + 0x66666666; // force nibble carry when BCD digit > 9
t1 = x ^ y; // bit-wise sum
t0 = t0 + y; // addition with nibble carry
t1 = t1 ^ t0; // (x ^ y) ^ (x + y)
t0 = t0 < y; // capture carry-out from bit 31
t1 = (t1 >> 1) | (t0 << 31); // nibble carry-outs in bits 3, 7, ..., 31
t0 = t1 & 0x88888888; // extract nibble carry-outs
t1 = t0 >> 2; // 8 - (8 >> 2) = 6
return x + y + (t0 - t1); // add 6 to any digit with nibble carry-out
}
Knuth, TAOCP Vol.4A Part 1, offers a superior solution (requiring fewer operations) in the answer to exercise 100 from section 7.1.3. This variant is particularly well suited to processor architectures with an instruction that can evaluate any logical function of three arguments, such as the LOP3
instruction of modern NVIDIA GPUs.
uint32_t median (uint32_t x, uint32_t y, uint32_t z)
{
return (x & (y | z)) | (y & z);
}
uint32_t bcd_add_knuth (uint32_t x, uint32_t y)
{
uint32_t z, u, t;
z = y + 0x66666666;
u = x + z;
t = median (~x, ~z, u) & 0x88888888;
return u - t + (t >> 2);
}

- 23,970
- 4
- 78
- 130