Pergunta

Eu sei que você pode usar esta tabela para decimal convertido para BCD:

0 0000

1 0001

2 0010

3 0011

4 0100

5 0101

6 0110

7 0111

8 1000

9 1001

Existe uma equação para esta conversão ou você tem que usar apenas a mesa? Estou tentando escrever algum código para esta conversão, mas não tenho a certeza como fazer a matemática para ele. Sugestões?

Foi útil?

Solução

Você sabe que a Binary numeral sistema , não é?

Especialmente ter um olhar para neste capítulo .

EDIT: Além disso, observe o comentário de KFro que os menores mordidela (= 4 bits) da representação ASCII binário de números está em BCD. Isso faz conversões BCD <-> ASCII muito fácil, pois você só tem que adicionar / remover os principais 4 bits:

Number    ASCII Code
0         0011 0000
1         0011 0001
 ...
8         0011 1000
9         0011 1001

Outras dicas

#include <stdint.h>

/* Standard iterative function to convert 16-bit integer to BCD */
uint32_t dec2bcd(uint16_t dec) 
{
    uint32_t result = 0;
    int shift = 0;

    while (dec)
    {
        result +=  (dec % 10) << shift;
        dec = dec / 10;
        shift += 4;
    }
    return result;
}

/* Recursive one liner because that's fun */
uint32_t dec2bcd_r(uint16_t dec)
{
    return (dec) ? ((dec2bcd_r( dec / 10 ) << 4) + (dec % 10)) : 0;
}

Esta é a partir do mundo micro controlador .... Note que os valores são arredondados na divisão. Por exemplo 91 a BCD seria 91/10 * 16 = 144 + 91% 10 = 145. convertido para binário é 10010001.

uint8_t bcdToDec(uint8_t val)
{
  return ( (val/16*10) + (val%16) );
}

uint8_t decToBcd(uint8_t val)
{
  return ( (val/10*16) + (val%10) );
}

Normalmente, quando alguém diz que quer converter de decimal para BCD, eles estão falando mais de um dígito decimal.

BCD é muitas vezes embalado em dois dígitos decimais por byte (porque 0..9 ajuste em 4 bits, como você mostrou), mas eu acho que é mais natural para usar uma matriz de bytes, um por dígito decimal.

Um n bits número binário unsigned vai encaixar-ceil (N * log_2 (10)) = ceil dígitos (n / log 10 (2)) decimais. É também vai encaixar ceil (n / 3) = andar ((n + 2) / 3)) dígitos decimais, desde 2 ^ 3 = 8 é menos do que 10.

Com isso em mente, aqui está como eu pegaria os dígitos decimais de um int não assinado:

#include <algorithm>
#include <vector>

template <class Uint>
std::vector<unsigned char> bcd(Uint x) {  
  std::vector<unsigned char> ret;
  if (x==0) ret.push_back(0); 
  // skip the above line if you don't mind an empty vector for "0"
  while(x>0) {
    Uint d=x/10;
    ret.push_back(x-(d*10)); // may be faster than x%10
    x=d;
  }
  std::reverse(ret.begin(),ret.end());
  // skip the above line if you don't mind that ret[0] is the least significant digit
  return ret;
}

Claro que, se você sabe a largura do tipo int, você pode preferir matrizes de comprimento fixo. Também não há razão para reverter a todos, se você pode se lembrar o fato de que o dígito 0 é o menos significativo e reverter apenas em entrada / saída. Mantendo o dígito menos significativo como os primeiros simplifica ops aritméticas dígitos-wise no caso que você não use um número fixo de dígitos.

Se você quiser representar "0" como o "0" dígito decimal única, em vez do dígito string vazia (tanto é válido), então você verificar especificamente x == 0.

Se você quiser dois dígitos decimais por byte, e "sem sinal" é metade do tamanho de "unsigned long" (uint32 uso e UINT64 typedefs se você quiser):

unsigned long bcd(unsigned x) {
  unsigned long ret=0;
  while(x>0) {
    unsigned d=x/10;
    ret=(ret<<4)|(x-d*10);
    x=d;
  }
  return ret;
}

Isso deixa você com o dígito menos significativo (unidade) decimal no meio-byte menos significativo. Você também pode executar o loop um número fixo (10 para uint32) de vezes, não parando cedo, quando apenas 0 bits são deixadas, o que permitiria o otimizador a desenrolá-lo, mas isso é mais lento se os seus números são muitas vezes lento.

Será que algo como este trabalho para a sua conversão?

#include <string>
#include <bitset>

using namespace std;

string dec_to_bin(unsigned long n)
{
    return bitset<numeric_limits<unsigned long>::digits>(n).to_string<char, char_traits<char>, allocator<char> >();
}

Este código codifica e decodifica. Benchmarks são os seguintes.

  • 45 relógios para o round-trip
  • 11 relógios para desembalar BCD para uint32_t
  • 34 relógios para embalagem uint32_t em BCD

Eu usei um uint64_t para armazenar o BCD aqui. espaço muito conveniente e largura fixa, mas não muito eficiente para grandes tabelas. Embalar os dígitos BCD, 2 para char [] para que.

// -------------------------------------------------------------------------------------
uint64_t uint32_to_bcd(uint32_t usi)    {

    uint64_t shift = 16;  
    uint64_t result = (usi % 10);

    while (usi = (usi/10))  {
        result += (usi % 10) * shift;
        shift *= 16; // weirdly, it's not possible to left shift more than 32 bits
    }
    return result;
}
// ---------------------------------------------------------------------------------------
uint32_t bcd_to_ui32(uint64_t bcd)  {

    uint64_t mask = 0x000f;
    uint64_t pwr = 1;

    uint64_t i = (bcd & mask);
    while (bcd = (bcd >> 4))    {
        pwr *= 10;
        i += (bcd & mask) * pwr;
    }
    return (uint32_t)i;
}
// --------------------------------------------------------------------------------------
const unsigned long LOOP_KNT = 3400000000; // set to clock frequencey of your CPU
// --------------------------------------------------------------------------------------
int main(void)  {
    time_t start = clock();
    uint32_t foo, usi = 1234; //456;
    uint64_t result;
    unsigned long i;

    printf("\nRunning benchmarks for %u loops.", LOOP_KNT);

    start = clock();
    for (uint32_t i = 0; i < LOOP_KNT; i++) {
        foo = bcd_to_ui32(uint32_to_bcd(i >> 10));
    }
    printf("\nET for bcd_to_ui32(uint_16_to_bcd(t)) was %f milliseconds. foo %u", (double)clock() - start, foo);


    printf("\n\nRunning benchmarks for %u loops.", LOOP_KNT);

    start = clock();
    for (uint32_t i = 0; i < LOOP_KNT; i++) {
        foo = bcd_to_ui32(i >> 10);
    }
    printf("\nET for bcd_to_ui32(uint_16_to_bcd(t)) was %f milliseconds. foo %u", (double)clock() - start, foo);

    getchar();
    return 0;
}

NOTA: Parece que é impossível, mesmo com ints de 64 bits, a mudança deixou mais de 32 bits, mas, felizmente, é perfeitamente possível multiplicar por algum fator de 16 - que felizmente tem o efeito desejado. É também muito mais rápido. Vai figura.

Eu sei que este tenha sido previamente respondida, mas eu já estendeu essa para ints não assinados de tamanhos diferentes usando um modelo para construir o código específico.

#include <stdio.h>
#include <unistd.h>

#include <stdint.h>

#define __STDC_FORMAT_MACROS
#include <inttypes.h>

constexpr int nBCDPartLength = 4;
constexpr int nMaxSleep = 10000; // Wait enough time (in ms) to check out the boundry cases before continuing.

// Convert from an integer to a BCD value.
// some ideas for this code are from :
//  http://stackoverflow.com/questions/1408361/unsigned-integer-to-bcd-conversion
//  &&
//  http://stackoverflow.com/questions/13587502/conversion-from-integer-to-bcd
// Compute the last part of the information and place it into the result location.
// Decrease the original value to place the next lowest digit into proper position for extraction.
template<typename R, typename T> R IntToBCD(T nValue) 
{
    int nSizeRtn = sizeof(R);
    char acResult[nSizeRtn] {};
    R nResult { 0 };
    int nPos { 0 };

    while (nValue)
    {
        if (nPos >= nSizeRtn)
        {
            return 0;
        }

        acResult[nPos] |= nValue % 10;
        nValue /= 10;

        acResult[nPos] |= (nValue % 10) << nBCDPartLength;
        nValue /= 10;

        ++nPos;
    }

    nResult = *(reinterpret_cast<R *>(acResult));

    return nResult;
}

int main(int argc, char **argv)
{
    //uint16_t nValue { 10 };
    //printf("The BCD for %d is %x\n", nValue, IntToBCD<uint32_t, uint16_t>(nValue));

    // UINT8_MAX    =   (255)                               - 2 bytes can be held in uint16_t (2 bytes)
    // UINT16_MAX   =   (65535)                             - 3 bytes can be held in uint32_t (4 bytes)
    // UINT32_MAX   =   (4294967295U)                       - 5 bytes can be held in uint64_t (8 bytes)
    // UINT64_MAX   =   (__UINT64_C(18446744073709551615))  - 10 bytes can be held in uint128_t (16 bytes)


    // Test edge case for uint8
    uint8_t n8Value { UINT8_MAX - 1 };
    printf("The BCD for %u is %x\n", n8Value, IntToBCD<uint16_t, uint8_t>(n8Value));
    // Test edge case for uint16
    uint16_t n16Value { UINT16_MAX - 1 };
    printf("The BCD for %u is %x\n", n16Value, IntToBCD<uint32_t, uint16_t>(n16Value));
    // Test edge case for uint32
    uint32_t n32Value { UINT32_MAX - 1 };
    printf("The BCD for %u is %" PRIx64 "\n", n32Value, IntToBCD<uint64_t, uint32_t>(n32Value));
    // Test edge case for uint64
    uint64_t n64Value { UINT64_MAX - 1 };
    __uint128_t nLargeValue = IntToBCD<__uint128_t, uint64_t>(n64Value);
    uint64_t nTopHalf = uint64_t(nLargeValue >> 64);
    uint64_t nBottomHalf = uint64_t(nLargeValue);
    printf("The BCD for %" PRIu64 " is %" PRIx64 ":%" PRIx64 "\n", n64Value, nTopHalf, nBottomHalf);

    usleep(nMaxSleep);

    // Test all the values
    for (uint8_t nIdx = 0; nIdx < UINT8_MAX; ++nIdx)
    {
        printf("The BCD for %u is %x\n", nIdx, IntToBCD<uint16_t, uint8_t>(nIdx));
    }

    for (uint16_t nIdx = 0; nIdx < UINT16_MAX; ++nIdx)
    {
        printf("The BCD for %u is %x\n", nIdx, IntToBCD<uint32_t, uint16_t>(nIdx));
    }

    for (uint32_t nIdx = 0; nIdx < UINT32_MAX; ++nIdx)
    {
        printf("The BCD for %u is %" PRIx64 "\n", nIdx, IntToBCD<uint64_t, uint32_t>(nIdx));
    }

    for (uint64_t nIdx = 0; nIdx < UINT64_MAX; ++nIdx)
    {
        __uint128_t nLargeValue = IntToBCD<__uint128_t, uint64_t>(nIdx);
        uint64_t nTopHalf = uint64_t(nLargeValue >> 64);
        uint64_t nBottomHalf = uint64_t(nLargeValue);
        printf("The BCD for %" PRIu64 " is %" PRIx64 ":%" PRIx64 "\n", nIdx, nTopHalf, nBottomHalf);
    }
    return 0;
}

Aqui está uma macro para uint16_t, para que ele seja avaliado em tempo de compilação (desde que u é uma constante pré-definida). Isto concorda com dec2bcd () lá de cima a 9999.

#define U16TOBCD(u) ((((u/1000)%10)<<12)|(((u/100)%10)<<8)|\
                    (((u/10)%10)<<4)|(u%10))

Apenas simplificado-lo.

#include <math.h>
#define uint unsigned int

uint Convert(uint value, const uint base1, const uint base2)
{
    uint result = 0;
    for (int i = 0; value > 0; i++)
    {
        result += value % base1 * pow(base2, i);
        value /= base1;
    }
    return result;
}

uint FromBCD(uint value)
{
    return Convert(value, 16, 10);
}

uint ToBCD(uint value)
{
    return Convert(value, 10, 16);
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top