Pregunta

Sé que puede usar esta tabla para convertir decimal a BCD:

0 0000

1 0001

2 0010

3 0011

4 0100

5 0101

6 0110

7 0111

8 1000

9 1001

¿Hay una ecuación para esta conversión o solo tienes que usar la tabla? Estoy tratando de escribir algún código para esta conversión, pero no estoy seguro de cómo hacer los cálculos. ¿Sugerencias?

¿Fue útil?

Solución

Conoces el Sistema de numeración binario , ¿no?

Especialmente eche un vistazo a este capítulo .

EDITAR: Observe también el comentario de KFro de que el mordisco inferior (= 4 bits) de la representación binaria ASCII de los números está en BCD. Esto hace conversiones BCD & Lt; - & Gt; ASCII es muy fácil ya que solo tiene que agregar / eliminar los 4 bits principales:

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

Otros consejos

#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;
}

Esto es del mundo del microcontrolador ... Tenga en cuenta que los valores se redondean en la división. Por ejemplo, 91 a BCD sería 91/10 * 16 = 144 + 91% 10 = 145. Convertido a Binario es 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) );
}

Por lo general, cuando alguien dice que quiere convertir de decimal a BCD, está hablando de más de un dígito decimal.

BCD a menudo se empaqueta en dos dígitos decimales por byte (porque 0..9 cabe en 4 bits, como ha mostrado), pero creo que es más natural usar una matriz de bytes, uno por dígito decimal.

Un número binario sin signo de n bits se ajustará en ceil (n * log_2 (10)) = ceil (n / log10 (2)) dígitos decimales. También cabe en ceil (n / 3) = piso ((n + 2) / 3)) dígitos decimales, ya que 2 ^ 3 = 8 es menor que 10.

Con eso en mente, así es como obtendría los dígitos decimales de un int sin signo:

#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;
}

Por supuesto, si conoce el ancho de su tipo int, puede preferir matrices de longitud fija. Tampoco hay ninguna razón para revertir en absoluto si puede recordar el hecho de que el dígito 0 es el menos significativo y revertir solo en la entrada / salida. Mantener el dígito menos significativo como el primero simplifica las operaciones aritméticas a nivel de dígitos en el caso de que no use un número fijo de dígitos.

Si quiere representar " 0 " como el único " 0 " dígito decimal en lugar de la cadena de dígitos vacía (cualquiera de las dos es válida), entonces verificaría específicamente para x == 0.

Si desea dos dígitos decimales por byte, y " unsigned " es la mitad del tamaño de " unsigned long " (use uint32 y uint64 typedefs si lo desea):

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;
}

Esto lo deja con el dígito decimal menos significativo (unidad) en el medio byte menos significativo. También puede ejecutar el bucle un número fijo (10 para uint32) de veces, sin detenerse temprano cuando solo quedan 0 bits, lo que permitiría al optimizador desenrollarlo, pero eso es más lento si sus números son a menudo lentos.

¿Funcionaría algo como esto para su conversión?

#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 y decodifica. Los puntos de referencia son los siguientes.

  • 45 relojes para el viaje de ida y vuelta
  • 11 relojes para desempacar BCD en uint32_t
  • 34 relojes para empaquetar uint32_t en BCD

Usé un uint64_t para almacenar el BCD aquí. Ancho muy conveniente y fijo, pero poco eficiente en espacio para mesas grandes. Empaquete los dígitos BCD, 2 en char [] para eso.

// -------------------------------------------------------------------------------------
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 es imposible, incluso con entradas de 64 bits, desplazarse a la izquierda más de 32 bits, pero afortunadamente, es completamente posible multiplicarlo por algún factor de 16, que felizmente tiene el efecto deseado. También es mucho más rápido. Vaya figura.

Sé que esto ha sido respondido previamente, pero lo he extendido para entradas sin firmar de diferentes tamaños usando una plantilla para construir el 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;
}

Aquí hay una macro para uint16_t, de modo que se evalúa en tiempo de compilación (siempre que u sea una constante predefinida). Esto concuerda con dec2bcd () desde arriba hasta 9999.

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

Simplemente lo simplifiqué.

#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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top