Fixed BCD code.

A

August Derleth

Cause I made such a hash of my first attempt, and in an effort to provide
PORTABLE code to solve the problem, I submit this solution to convert to
and from BCD values stored in longs. This should work just as well for
ints and (in theory) chars and, indeed, any integral type. If it doesn't,
suggest ways to improve it, even at a significant speed/code size penalty.

#include <math.h>

long bcd(long decimal)
{
int i;
long result = 0;

for(i = 0; decimal; ++i) {
result += (decimal % 10) * (int) pow(16,i);
decimal /= 10;
}

return(result);
}

long dec(long bcd)
{
long result = 0;
int i;

for(i = 0; bcd; ++i) {
result += (bcd % 16) * (int) pow(10,i);
bcd /= 16;
}

return(result);
}

/* Code tested on all of one machine, but C is portable in theory. */
 
B

Brett Frankenberger

Cause I made such a hash of my first attempt, and in an effort to provide
PORTABLE code to solve the problem, I submit this solution to convert to
and from BCD values stored in longs. This should work just as well for
ints and (in theory) chars and, indeed, any integral type. If it doesn't,
suggest ways to improve it, even at a significant speed/code size penalty.

#include <math.h>

long bcd(long decimal)
{
int i;
long result = 0;

for(i = 0; decimal; ++i) {
result += (decimal % 10) * (int) pow(16,i);

pow(16, i) returns a double, which you then cast to an int. There is
no guarantee that either has as much precision as a long. Instead:
result += (decimal %10) << (4 * i);
(Also offers the performance benefit of not converting back and forth
between floating point and integer.)

For similar reasons:

long dec (long bcd)
{
long result = 0, i = 1;

while (bcd) {
result += bcd % 16 * i;
i *= 10;
bcd /= 16;
};
}
/* Code tested on all of one machine, but C is portable in theory. */

Only when you don't make assumptions about implementation-defined
portions of the standard.

-- Brett
 
A

August Derleth

(e-mail address removed) (Brett Frankenberger) wrote in
on Fri 05 Sep 2003 09:04:00p:
pow(16, i) returns a double, which you then cast to an int. There is
no guarantee that either has as much precision as a long. Instead:
result += (decimal %10) << (4 * i);

Ah, but will the shifts be portable to machines that use, say, multiples
of three for their word-size? Will my multiplies?

I can't think of how BCD would work in an octal system (where you'd only
have three bits to encode each digit*), or on a hypothetical ternary
system (which /good/ C code is supposed to be portable to).

Is the problem itself limited to certain kinds of systems?

*Three bits only gives you eight numbers. Nine bits is 8+1, giving you the
possibility of encoding one `traditional' BCD digit with the option of a
sign bit. You can do the math from there.
Only when you don't make assumptions about implementation-defined
portions of the standard.

Touche'.
 
I

Irrwahn Grausewitz

pow(16, i) returns a double, which you then cast to an int. There is
no guarantee that either has as much precision as a long. Instead:
result += (decimal %10) << (4 * i);
Looks very familiar to me, as it's almost exactly what I proposed in the
original "decimal to BCD for long int" thread:

result |= ( dec % 10 ) << ( a * 4 );
(Also offers the performance benefit of not converting back and forth
between floating point and integer.)

For similar reasons:

long dec (long bcd)
{
long result = 0, i = 1;

while (bcd) {
result += bcd % 16 * i;
i *= 10;
bcd /= 16;
};
}
That's a an improvement now, I used to provide a simple 'ulpow()'
function. 'i *= 10;' definetely looks better...
 
I

Irrwahn Grausewitz

August Derleth said:
(e-mail address removed) (Brett Frankenberger) wrote in
on Fri 05 Sep 2003 09:04:00p:


Ah, but will the shifts be portable to machines that use, say, multiples
of three for their word-size?

What's got the size of a word have to do with the fact that you need at
least four bits to represent each digit in BCD? You /have/ to shift
left at least four bits, indespite of the actual machines word size.
Will my multiplies?

Not sure. But still: BCD is a way to get around floating point math, so
it's crazy (and overkill, and error prone) to use FPmath to implement
BCD...
I can't think of how BCD would work in an octal system (where you'd only
have three bits to encode each digit*),
Huh?

or on a hypothetical ternary system ...

If it's not on a machine based on binary numerical system, it just
not BCD. How many 'trits' would you need (at least) to encode a decimal
on a ternary machine? My guess: three.
...(which /good/ C code is supposed to be portable to).

Albeit I'm not sure what would happen to bitwise operators like |,&,^,~,
the bitwise shift operators would probably just become 'tritwise' shift
operators.
Is the problem itself limited to certain kinds of systems?

If the problem is 'how to en-/decode BCDs on system XY, yes: it's
limited to binary systems.
*Three bits only gives you eight numbers. Nine bits is 8+1, giving you the
possibility of encoding one `traditional' BCD digit with the option of a
sign bit.

Parse error: semantic reference mismatch. Sorry.
You can do the math from there.

No, thanks. :)

Irrwahn
 
B

Brett Frankenberger

(e-mail address removed) (Brett Frankenberger) wrote in
on Fri 05 Sep 2003 09:04:00p:


Ah, but will the shifts be portable to machines that use, say, multiples
of three for their word-size?

Yes. Assuming no overflows, shifting left 4 bits is the same as
multiplying by 16. Word-size is completely irrelevant.
Will my multiplies?

See my comment above. Your multiplies will be fine as long as you
don't run out of precision. And, again, word-size being a multiple of
3, or a multiple of something else, or both, has nothing to do with it.
I can't think of how BCD would work in an octal system (where you'd only
have three bits to encode each digit*), or on a hypothetical ternary
system (which /good/ C code is supposed to be portable to).

C integers are required to act like they have a binary representation.
Certainly you could implement that on a ternary system, but, for
example, <<1 would still have to multiple by 2, not 3, so it couldn't
be implemented as an actual shift on a ternary system.)

-- Brett
 
A

August Derleth

36:47a:
Looks very familiar to me, as it's almost exactly what I proposed in the
original "decimal to BCD for long int" thread:

result |= ( dec % 10 ) << ( a * 4 );

Heh. I didn't see that branch of the thread. I was more interested in
those reaming me out for posting crappy code. :)
That's a an improvement now, I used to provide a simple 'ulpow()'
function. 'i *= 10;' definetely looks better...

I think it's a good improvement, and I'm surprised it eluded both of us.
It should have been obvious (taking something to a power is only iterated
multiplication anyway), but instead we relied on <math.h>. Chuck Moore
would be disappointed.
 
I

Irrwahn Grausewitz

August Derleth said:
36:47a:

I think it's a good improvement, and I'm surprised it eluded both of us.
It should have been obvious (taking something to a power is only iterated
multiplication anyway), but instead we relied on <math.h>.
^^^^^^^^^^^^^^^^^^^^^
Well, you did. I was bold enough to write a function for
calculating powers of integer numbers... /me dumba, hehe. :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,781
Messages
2,569,615
Members
45,295
Latest member
EmilG1510

Latest Threads

Top