将整数从(纯)二进制转换为BCD

Convert integer from (pure) binary to BCD

提问人:Sagi 提问时间:11/6/2012 最后编辑:Daniel GehrigerSagi 更新时间:2/9/2022 访问量:51813

问:

我现在要傻傻地解决这个问题......

我得到一个 BCD 编号(每个数字都是自己的 4Bit 表示)

例如,我想要什么:

  • 输入: 202 (hex) == 514 (dec)
  • 输出: BCD 0x415

  • 输入:0x202

  • 位表示:0010 0000 0010 = 514

我试过什么:

unsigned int uiValue = 0x202;
unsigned int uiResult = 0;
unsigned int uiMultiplier = 1;
unsigned int uiDigit = 0;


// get the dec bcd value
while ( uiValue > 0 )
{
    uiDigit= uiValue & 0x0F;
    uiValue >>= 4;
    uiResult += uiMultiplier * uiDigit;
    uiMultiplier *= 10;
}

但我知道这是非常错误的,这将是位表示的 202,然后分成 5 个半字节,然后再次表示为十进制数

我可以在纸上解决问题,但我无法在简单的 C 代码中得到它

C 二进制 BCD

评论

1赞 Andrew 11/6/2012
我是否可以建议,在处理字节和半字节时,十六进制掩码更容易放入上下文中?例如,0x0F 15 更明显(至少对我来说是这样!
0赞 Sagi 11/6/2012
我添加了更多信息
0赞 Daniel Gehriger 11/6/2012
你的代码对我来说似乎很好。十进制 202 是二进制 1100 1010,或者,拆分为半字节:十进制 6 4,因此是十进制 64 的 BCD 表示。
1赞 Daniel Gehriger 11/6/2012
@Andrew:但是他说,不是。如果它是0x202,那么 BCD 值将是十进制 202......uiValue = 2020x202...
1赞 Daniel Gehriger 11/6/2012
@Sagi:因此,我的回答如下......

答:

1赞 Andrew 11/6/2012 #1

这里真正的问题是基地和单位的混淆

202 应该是十六进制,相当于 514 十进制......因此 BCD 计算是正确的

二进制代码十进制会将十进制 (514) 转换为三个半字节大小的字段: - 5 = 0101 - 1 = 0001 - 4 = 0100

更大的问题是,你的标题是错误的,你正在将 Uint 转换为 BCD,而标题要求将 BCD 转换为 Unint

评论

0赞 Daniel Gehriger 11/6/2012
安德鲁,没有那么复杂。无需将十六进制转换为十进制即可查找 BCD 值。
0赞 Andrew 11/6/2012
不,但我认为这解释了这种混乱......0x202 == 514(bcd) 而 202(dec) == 202(bcd)
0赞 nos 11/6/2012
@Andrew 相反,0x202 == 202 (bcd)。
0赞 Andrew 11/6/2012
志让我们更简单......10d = 0Ah = (0001 0000)BCD == “10”bcd 不是“0A”bcd...那么0x202怎么能== 202 BCD?
0赞 nos 11/6/2012
@Andrew 10d = 0Ah 的二进制表示为 0000 1010 ,所以我想这取决于他的 202 是否表示 bcd、十进制或十六进制。当然是错误的,尽管文本说它应该是十六进制:如果输入像文本所说的那样0x202,则输入的二进制表示是 0000 0010 0000 0010,我们可以很容易地将其转换为 bcd。uiValue = 202Input: 202 (hex)
15赞 Daniel Gehriger 11/6/2012 #2

你弄错了。您的代码正在从 BCD 转换为二进制文件,就像您的问题(原始)标题所说的那样。但是,仅当从二进制转换为 BCD 时,您提供的输入和输出值才是正确的。在这种情况下,请尝试:

#include <stdio.h>

int main(void) {

   int binaryInput = 0x202; 
   int bcdResult = 0;
   int shift = 0;

   printf("Binary: 0x%x (dec: %d)\n", binaryInput , binaryInput );

   while (binaryInput > 0) {
      bcdResult |= (binaryInput % 10) << (shift++ << 2);
      binaryInput /= 10;
   }

   printf("BCD: 0x%x (dec: %d)\n", bcdResult , bcdResult );
   return 0;
}

证明:http://ideone.com/R0reQh

评论

0赞 Sagi 11/6/2012
谢谢,这就是我需要的:)它几乎与我的代码相同,但我走错了方向......谢谢
0赞 PapaAtHome 10/22/2013
对不起,我之前的评论是错误的,但我无法删除它。
0赞 frr 5/16/2017
非常感谢,这对我有帮助。一个小提示:不确定这是否是您的目标,但输出 BCD 半字节的顺序是“最不重要”的。就我而言,我希望蚕食“最重要”,所以我必须将每个剩下的蚕食移位 4 的递增倍数(取模后立即移位),然后才将预移位的蚕食移到结果。请注意算法 (%10, /=10) 如何从最低阶十进制数字开始,然后继续到高阶。
0赞 Daniel Gehriger 5/18/2017
@frr - 你当然是对的。这是一个简单的错误,我修复了代码。感谢您的举报!
1赞 thomasrutter 8/22/2021
对于除法指令成本较高的平台(可以说是所有指令),您可以将 to 重构为 a,并将 binaryInput 中最多 9 的任何余数添加到最终值中,而无需再次除法。while (binaryInput > 0)while (binaryInput > 9)
0赞 panda-34 11/6/2012 #3

一个幼稚但简单的解决方案:

char buffer[16];
sprintf(buffer, "%d", var);
sscanf(buffer, "%x", &var);

评论

0赞 fkl 11/6/2012
这不会产生十六进制吗,正如 OP 要求的那样 BCD?它们不等同。不过我没有投反对票。这是别的身体
0赞 panda-34 11/6/2012
从他问题的第一次修订开始,很明显他想将 202 转换为 514,它就是这样做的。
0赞 Andrew 11/6/2012
但他想要的是 514 BCD 而不是 514 Hex
0赞 panda-34 11/6/2012
@Andrew,他开始希望在我回答之后,我回答了第一次修订,他说只有输入在 BCD 中
2赞 panda-34 11/6/2012
@nos,我不知道,我应该回答标题、问题正文或示例代码中的问题。它们是三个不同的。
2赞 enthusiasticgeek 12/11/2012 #4

请尝试以下操作。

unsigned long toPackedBcd (unsigned int val)
{
  unsigned long bcdresult = 0; char i;


  for (i = 0; val; i++)
  {
    ((char*)&bcdresult)[i / 2] |= i & 1 ? (val % 10) << 4 : (val % 10) & 0xf;
    val /= 10;
  }
  return bcdresult;
}

也可以尝试以下变体(尽管可能效率低下)

/*
Copyright (c) 2016 enthusiasticgeek<[email protected]> Binary to Packed BCD
This code may be used (including commercial products) without warranties of any kind (use at your own risk)
as long as this copyright notice is retained.
Author, under no circumstances, shall not be responsible for any code crashes or bugs.
Exception to copyright code: 'reverse string function' which is taken from http://stackoverflow.com/questions/19853014/reversing-a-string-in-place-in-c-pointers#19853059
Double Dabble Algorithm for unsigned int explanation

255(binary) - base 10 -> 597(packed BCD) - base 16
     H|    T|    U|        (Keep shifting left)
               11111111
             1 1111111
            11 111111  
           111 11111
          1010 11111    <-----added 3 in unit's place (7+3 = 10) 
        1 0101 1111  
        1 1000 1111     <-----added 3 in unit's place (5+3 = 8)
       11 0001 111
      110 0011 11       
     1001 0011 11       <-----added 3 in ten's place (6+3 = 9)
   1 0010 0111 1  
   1 0010 1010 1        <-----added 3 in unit's place (7+3 = 10)
  10 0101 0101  -> binary 597 but bcd 255
  ^    ^    ^  
  |    |    |
  2    5    5   
*/
#include <stdio.h>   
#include <string.h>

//Function Prototypes
unsigned int binaryToPackedBCD (unsigned int binary); 
char * printPackedBCD(unsigned int bcd, char * bcd_string);

// For the following function see http://stackoverflow.com/questions/19853014/reversing-a-string-in-place-in-c-pointers#19853059
void reverse(char *str);

//Function Definitions
unsigned int binaryToPackedBCD (unsigned int binary) {
  const unsigned int TOTAL_BITS = 32;
  /*Place holder for bcd*/
  unsigned int bcd = 0;
  /*counters*/
  unsigned int i,j = 0;
  for (i=0; i<TOTAL_BITS; i++) {
     /*
      Identify the bit to append  to LSB of 8 byte or 32 bit word -
      First bitwise AND mask with 1. 
      Then shift to appropriate (nth shift) place. 
      Then shift the result back to the lsb position. 
     */
      unsigned int binary_bit_to_lsb = (1<<(TOTAL_BITS-1-i)&binary)>>(TOTAL_BITS-1-i);
      /*shift by 1 place and append bit to lsb*/
      bcd = ( bcd<<1 ) | binary_bit_to_lsb;       
      /*printf("=> %u\n",bcd);*/
      /*Don't add 3 for last bit shift i.e. in this case 32nd bit*/
      if( i >= TOTAL_BITS-1) { 
      break;
      }
      /*else continue*/
      /* Now, check every nibble from LSB to MSB and if greater than or equal 5 - add 3 if so */
      for (j=0; j<TOTAL_BITS; j+=4) {
        unsigned int temp = (bcd & (0xf<<j))>>j;
        if(temp >= 0x5) {
        /*printf("[%u,%u], %u, bcd = %u\n",i,j, temp, bcd);*/
        /*Now, add 3 at the appropriate nibble*/
         bcd = bcd  + (3<<j);
        // printf("Now bcd = %u\n", bcd);
        }
      }
  }
  /*printf("The number is %u\n",bcd);*/
  return bcd;
}   

char * printPackedBCD(unsigned int bcd, char * bcd_string) {
  const unsigned int TOTAL_BITS = 32;
  printf("[LSB] =>\n");
   /* Now, check every nibble from LSB to MSB and convert to char* */
  for (unsigned int j=0; j<TOTAL_BITS; j+=4) {
  //for (unsigned int j=TOTAL_BITS-1; j>=4; j-=4) {
      unsigned int temp = (bcd & (0xf<<j))>>j;
      if(temp==0){
    bcd_string[j/4] = '0';      
      } else if(temp==1){
    bcd_string[j/4] = '1';
      } else if(temp==2){
    bcd_string[j/4] = '2';
      } else if(temp==3){
    bcd_string[j/4] = '3';
      } else if(temp==4){
    bcd_string[j/4] = '4';
      } else if(temp==5){
    bcd_string[j/4] = '5';
      } else if(temp==6){
    bcd_string[j/4] = '6';
      } else if(temp==7){
    bcd_string[j/4] = '7';
      } else if(temp==8){
    bcd_string[j/4] = '8';
      } else if(temp==9){
    bcd_string[j/4] = '9';
      } else {
    bcd_string[j/4] = 'X';
      }
      printf ("[%u - nibble] => %c\n", j/4, bcd_string[j/4]);
  }      
  printf("<= [MSB]\n");
  reverse(bcd_string);
  return bcd_string;
}

// For the following function see http://stackoverflow.com/questions/19853014/reversing-a-string-in-place-in-c-pointers#19853059
void reverse(char *str)
{ 
    if (str != 0 && *str != '\0') // Non-null pointer; non-empty string
    {
    char *end = str + strlen(str) - 1; 
    while (str < end)
    {
        char tmp = *str; 
        *str++ = *end; 
        *end-- = tmp;
    } 
    }
}

int main(int argc, char * argv[])
{
  unsigned int number = 255;
  unsigned int bcd = binaryToPackedBCD(number);
  char bcd_string[8];
  printPackedBCD(bcd, bcd_string);
  printf("Binary (Base 10) = %u => Packed BCD (Base 16) = %u\n OR \nPacked BCD String = %s\n", number, bcd, bcd_string);
  return 0;
}

评论

0赞 Niklas Rosencrantz 3/30/2014
我测试了这个函数,它最初似乎可以工作,但我没有尝试过非常大的数字,也没有将其效率与其他候选算法进行比较,以进行转换,例如 或类似。(我的项目是 Altera DE2 FPGA 的 7 段 LED 的 C 代码。1117
0赞 Ernesto Flôres Barreira 2/18/2017 #5

这是我开发的解决方案,非常适合嵌入式系统,例如 Microchip PIC 微控制器:

#include <stdio.h>
void main(){
    unsigned int output = 0;
    unsigned int input;
    signed char a;
    //enter any number from 0 to 9999 here:
    input = 1265;
    for(a = 13; a >= 0; a--){
        if((output & 0xF) >= 5)
            output += 3;
        if(((output & 0xF0) >> 4) >= 5)
            output += (3 << 4);
        if(((output & 0xF00) >> 8) >= 5)
            output += (3 << 8);
        output = (output << 1) | ((input >> a) & 1);
    }
    printf("Input decimal or binary: %d\nOutput BCD: %X\nOutput decimal: %u\n", input, output, output);
}
0赞 Eduardo Prado 9/17/2020 #6

这是我的 n 字节转换版本:

//----------------------------------------------
// This function converts n bytes Binary (up to 8, but can be any size)
// value to n bytes BCD value or more.
//----------------------------------------------

void bin2bcdn(void * val, unsigned int8 cnt)
{
    unsigned int8  sz, y, buff[20];         // buff = malloc((cnt+1)*2);
    
    if(cnt > 8) sz = 64;                    // 8x8
    else        sz = cnt * 8 ;              // Size in bits of the data we shift
    
    memset(&buff , 0, sizeof(buff));        // Clears buffer
    memcpy(&buff, val, cnt);                // Copy the data to buffer

    while(sz && !(buff[cnt-1] & 0x80))      // Do not waste time with null bytes,
    {                                       // so search for first significative bit
        rotate_left(&buff, sizeof(buff));   // Rotate until we find some data
        sz--;                               // Done this one
    }
    while(sz--)                             // Anyting left?
    {
        for( y = 0; y < cnt+2; y++)         // Here we fix the nibbles
        {
            if(((buff[cnt+y] + 0x03) & 0x08) != 0) buff[cnt+y] += 0x03;
            if(((buff[cnt+y] + 0x30) & 0x80) != 0) buff[cnt+y] += 0x30;
        }
        rotate_left(&buff, sizeof(buff));   // Rotate the stuff
    }
    memcpy(val, &buff[cnt], cnt);           // Copy the buffer to the data
//  free(buff);       //in case used malloc
}   // :D Done
0赞 Hal Sampson 11/22/2020 #7
long bin2BCD(long binary) { // double dabble: 8 decimal digits in 32 bits BCD
  if (!binary) return 0;
  long bit = 0x4000000; //  99999999 max binary
  while (!(binary & bit)) bit >>= 1;  // skip to MSB

  long bcd = 0;
  long carry = 0;
  while (1) {
    bcd <<= 1;
    bcd += carry; // carry 6s to next BCD digits (10 + 6 = 0x10 = LSB of next BCD digit)
    if (bit & binary) bcd |= 1;
    if (!(bit >>= 1)) return bcd;
    carry = ((bcd + 0x33333333) & 0x88888888) >> 1; // carrys: 8s -> 4s
    carry += carry >> 1; // carrys 6s  
  }
}
0赞 Josko Marsic 11/26/2020 #8

简单的解决方案

#include <stdio.h>

int main(void) {

   int binaryInput = 514 ;      //0x202 
   int bcdResult = 0;
   int digit = 0;
   int i=1;

   printf("Binary: 0x%x (dec: %d)\n", binaryInput , binaryInput );

   while (binaryInput > 0) {
 
      digit = binaryInput %10;          //pick digit
      bcdResult = bcdResult+digit*i;
      i=16*i;
      binaryInput = binaryInput/ 10;
   }
   printf("BCD: 0x%x (dec: %d)\n", bcdResult , bcdResult );
   return 0;
}

二进制:0x202(十进制:514)

BCD: 0x514 (十二月: 1300)

1赞 Videosplicer 7/9/2021 #9

我的 2 美分,我需要类似的 RTC 芯片,它使用 BCD 对时间和日期信息进行编码。提出了以下适用于该要求的宏:

#define MACRO_BCD_TO_HEX(x) ((BYTE) ((x >> 4) & 0x0F) * 10) + (x & 0x0F)))

#define MACRO_HEX_TO_BCD(x) ((字节) (((x / 10 ) << 4) |((x % 10))))

0赞 NAND 2/9/2022 #10

您也可以尝试以下操作:

在每次迭代中,其余部分(表示为半字节)被放置在其相应的位置。

uint32_t bcd_converter(int num)
{                          
  uint32_t temp=0;              
  int i=0;                 
  while(num>0){            
    temp|=((num%10)<<i);   
    i+=4;                  
    num/=10;               
  }                        
                           
  return temp;             
}