Преобразование целого числа из (чистого) двоичного кода в BCD

Я слишком тупой сейчас, чтобы решить эту проблему...

Я получаю число BCD (каждая цифра является собственным 4-битным представлением)

Например, что я хочу:

  • Ввод: 202 (шестнадцатеричный) == 514 (десятичный)
  • Вывод: 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-коде.


person Sagi    schedule 06.11.2012    source источник
comment
Могу ли я предположить, что при работе с байтами и полубайтами шестнадцатеричные маски легче помещать в контекст? например, 0x0F более очевидно, чем 15 (по крайней мере, для меня!)   -  person Andrew    schedule 06.11.2012
comment
Я добавил еще немного информации   -  person Sagi    schedule 06.11.2012
comment
@ Эндрю, это правда, это изменится. Спасибо   -  person Sagi    schedule 06.11.2012
comment
Ваш код кажется мне очень хорошим. десятичное число 202 — это двоичное число 1100 1010, или, разделенное на части: десятичное число 6 4 и, следовательно, двоично-десятичное представление десятичного числа 64.   -  person Daniel Gehriger    schedule 06.11.2012
comment
202 hex = 514 dec = 514 BCD :)   -  person Andrew    schedule 06.11.2012
comment
@Эндрю: Но он сказал uiValue = 202, а не 0x202.... И если бы это было 0x202, то значение BCD было бы десятичным 202...   -  person Daniel Gehriger    schedule 06.11.2012
comment
@DanielGehriger: это правда, я тоже пришел к этому в своих попытках   -  person Sagi    schedule 06.11.2012
comment
@Sagi: Отсюда и мой ответ ниже...   -  person Daniel Gehriger    schedule 06.11.2012
comment
Я, должно быть, схожу с ума, потому что думал, что подсказка была в названии: Binary Coded DECIMAL, а не BChex :)   -  person Andrew    schedule 06.11.2012
comment
Саги - не могли бы вы уточнить: вы хотите преобразовать BCD в Uint (название вопроса) или Uint в BCD (код)   -  person Andrew    schedule 06.11.2012
comment
@Sagi, в комментариях к моему ответу продолжается обсуждение того, хотите ли вы на самом деле использовать BCD в качестве своего вывода (как указано в вашем вопросе) или обычный int (как это делает ваш заголовок вопроса, а также первая версия вашего вопроса). Пожалуйста, предоставьте двоичное представление правильного результата, который вы хотите для ввода 0010 0000 0010   -  person panda-34    schedule 06.11.2012


Ответы (8)


Вы поняли это неправильно. Ваш код преобразуется из 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

person Daniel Gehriger    schedule 06.11.2012
comment
Спасибо, это то, что мне было нужно :) это почти тот же код, что и у меня, но я пошел не в ту сторону... Спасибо - person Sagi; 06.11.2012
comment
Извините, мой предыдущий комментарий ошибочен, но я не могу его удалить. - person PapaAtHome; 22.10.2013
comment
Большое спасибо, это помогло мне. Одно маленькое примечание: не уверен, что это была ваша цель, но выходные полубайты двоично-десятичного кода сначала выводятся в порядке наименее значимого. В моем случае я хотел, чтобы откусы выходили первыми наиболее значимыми, поэтому мне пришлось сдвигать каждый оставшийся откусок на возрастающее число, кратное 4 (сдвиг сразу после взятия по модулю), и только затем ИЛИ предварительно сдвинутый откусыватель к результату. Обратите внимание, как алгоритм (%10, /=10) начинается с младших десятичных цифр и переходит к более высоким порядкам. - person frr; 16.05.2017
comment
@frr - вы, конечно, правы. Это была простая ошибка, и я исправил код. Спасибо за отчет! - person Daniel Gehriger; 17.05.2017

Попробуйте следующее.

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;
}
person enthusiasticgeek    schedule 10.12.2012
comment
Я тестирую эту функцию, и сначала она работает, но я не пробовал ее для очень больших чисел и не сравнивал ее эффективность с другим алгоритмом-кандидатом для преобразования, например, из 11 до 17 или аналогичным образом. (Мой проект — код C для 7-сегментных светодиодов Altera DE2 FPGA.) - person Niklas R.; 30.03.2014

Настоящая проблема здесь - путаница баз и юнитов

202 должно быть HEX, что соответствует десятичному числу 514... и, следовательно, расчеты BCD верны.

Десятичный двоичный код преобразует десятичное число (514) в три поля размера полубайта: - 5 = 0101 - 1 = 0001 - 4 = 0100

Более серьезная проблема заключалась в том, что у вас неправильное название, и вы конвертируете Uint в BCD, тогда как в заголовке запрашивалось BCD для Unint.

person Andrew    schedule 06.11.2012
comment
Андрей, это не так уж и сложно. Нет необходимости преобразовывать шестнадцатеричное число в десятичное, чтобы найти значение BCD. - person Daniel Gehriger; 06.11.2012
comment
НЕТ, но я думаю, что это объясняет путаницу... 0x202 == 514(bcd) тогда как 202(dec) == 202(bcd) - person Andrew; 06.11.2012
comment
@ Эндрю Это наоборот, 0x202 == 202 (BCD). - person nos; 06.11.2012
comment
А? Пойдем проще... 10d = 0Ah = (0001 0000)BCD == 10bcd, а не 0Abcd... так как же 0x202 == 202 BCD? - person Andrew; 06.11.2012
comment
@Andrew 10d = 0Ah имеет двоичное представление 0000 1010 , поэтому я думаю, это зависит от того, означает ли его 202 двоично-десятичный, десятичный или шестнадцатеричный формат. Конечно, uiValue = 202 неверно, хотя в тексте сказано, что оно должно быть шестнадцатеричным: Input: 202 (hex) Если ввод 0x202, как сказано в тексте, двоичное представление ввода будет 0000 0010 0000 0010, которое мы можем легко преобразовать в bcd. - person nos; 06.11.2012
comment
10 == 0x0A имеет двоичное представление 1010 да, но двоично-десятичное число 0001-0000. Это НЕ одно и то же - person Andrew; 06.11.2012

Мои 2 цента, мне нужно было подобное для чипа RTC, который использовал BCD для кодирования информации о времени и дате. Придумал следующие макросы, которые отлично работали для этого требования:

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

#define MACRO_HEX_TO_BCD(x) ((BYTE) (((x / 10 ) ‹‹ 4) | ((x % 10))))

person Videosplicer    schedule 08.07.2021

Наивное, но простое решение:

char buffer[16];
sprintf(buffer, "%d", var);
sscanf(buffer, "%x", &var);
person panda-34    schedule 06.11.2012
comment
Разве это не приведет к шестнадцатеричности, когда ОП запросил BCD? Они не эквивалентны. Я не проголосовал против, хотя. Это какое-то другое тело - person fkl; 06.11.2012
comment
Из первой редакции его вопроса было ясно, что он хочет преобразовать 202 в 514. Он именно это и делает. - person panda-34; 06.11.2012
comment
Но он хочет 514 BCD, а не 514 Hex. - person Andrew; 06.11.2012
comment
@Andrew, он начал хотеть, чтобы после моего ответа я ответил на первую ревизию, где он заявил, что только ввод был в BCD - person panda-34; 06.11.2012
comment
ОК, хотя его заголовок всегда говорил BCD ... но опять же его заголовок вопроса выглядит неправильно для вопроса - person Andrew; 06.11.2012
comment
@nos, я не знаю, должен ли я отвечать на вопрос в заголовке, в теле вопроса или в примере кода. Это три разных. - person panda-34; 06.11.2012
comment
@ panda-34: лично мне нравится ваше решение - и оно оказывается правильным, учитывая комментарий ОП к его принятому ответу. - person Daniel Gehriger; 06.11.2012

Это решение, которое я разработал, отлично работает для встроенных систем, таких как микроконтроллеры 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);
}
person Ernesto Flôres Barreira    schedule 17.02.2017

Это моя версия для преобразования 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
person Eduardo Prado    schedule 17.09.2020

Простое решение

#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)

person Josko Marsic    schedule 26.11.2020