I have the code below to convert a 32 bit BCD value (supplied in two uint halves) to a uint binary value.
The values supplied can be up to 0x9999, to form a maximum value of 0x99999999.
Is there a better (ie. quicker) way to achieve this?
/// <summary>
/// Convert two PLC words in BCD format (forming 8 digit number) into single binary integer.
/// e.g. If Lower = 0x5678 and Upper = 0x1234, then Return is 12345678 decimal, or 0xbc614e.
/// </summary>
/// <param name="lower">Least significant 16 bits.</param>
/// <param name="upper">Most significant 16 bits.</param>
/// <returns>32 bit unsigned integer.</returns>
/// <remarks>If the parameters supplied are invalid, returns zero.</remarks>
private static uint BCD2ToBin(uint lower, uint upper)
{
uint binVal = 0;
if ((lower | upper) != 0)
{
int shift = 0;
uint multiplier = 1;
uint bcdVal = (upper << 16) | lower;
for (int i = 0; i < 8; i++)
{
uint digit = (bcdVal >> shift) & 0xf;
if (digit > 9)
{
binVal = 0;
break;
}
else
{
binVal += digit * multiplier;
shift += 4;
multiplier *= 10;
}
}
}
return binVal;
}
-
Your code seems rather complicated; do you require the specific error checking?
Otherwise, you could just use the following code which shouldn't be slower, in fact, it's mostly the same:
uint result = 0; uint multiplier = 1; uint value = lo | hi << 0x10; while (value > 0) { uint digit = value & 0xF; value >>= 4; result += multiplier * digit; multiplier *= 10; } return result; -
I suppose you could unroll the loop:
value = ( lo & 0xF); value+= ((lo>>4) & 0xF) *10; value+= ((lo>>8) & 0xF) *100; value+= ((lo>>12)& 0xF) *1000; value+= ( hi & 0xF) *10000; value+= ((hi>>4 & 0xF) *100000; value+= ((hi>>8) & 0xF) *1000000; value+= ((hi>>12)& 0xF) *10000000;And you can check for invalid BCD digits like this:
invalid = lo & ((lo&0x8888)>>2)*3This sets invalid to a non-zero value if any single hex digit > 9.
epotter : This doesn't quite work. When you unroll the loop, you have to remember the "value >>= 4;" -
If you unroll the loop, remember to keep the bit shift.
value = ( lo & 0xF); value += ((lo >> 4 ) & 0xF) * 10; value += ((lo >> 8 ) & 0xF) * 100; value += ((lo >> 12) & 0xF) * 1000; value += ( hi & 0xF) * 10000; value += ((hi >> 4 ) & 0xF) * 100000; value += ((hi >> 8 ) & 0xF) * 1000000; value += ((hi >> 12) & 0xF) * 10000000;epotter : This should be combined with AShelly's answerAdam Rosenfield : Please put parens around the bit twiddling operators - the operator precedence for them is very confusing for most people.epotter : Good call. That makes it much more readable. -
If you've space to spare for a 39,322 element array, you could always just look the value up.
ScottS : For raw speed a look-up table is certainly going to win.GvS : The quickest solution so far. (he asked for it) -
Of course, there are a more efficient method. this is just a example of course, so you can tune it as a lesson ^^
function bcd_to_bin ($bcd) { $mask_sbb = 0x33333333; $mask_msb = 0x88888888; $mask_opp = 0xF; for($i=28;$i;--$i) { $mask_msb <<= 1; $mask_opp <<= 1; $mask_sbb <<= 1; for($j=0;$j<$i;$j+=4) { $mask_opp_j = $mask_opp << $j; if ($bcd & $mask_msb & $mask_opp_j ) { $bcd -= $mask_sbb & $mask_opp_j; } } } return $bcd;}
0 comments:
Post a Comment