Question

Je sais que vous pouvez utiliser ce tableau pour convertir le nombre décimal en BCD:

0 0000

1 0001

2 0010

3 0011

4 0100

5 0101

6 0110

7 0111

8 1000

9 1001

Existe-t-il une équation pour cette conversion ou devez-vous simplement utiliser le tableau? J'essaie d'écrire du code pour cette conversion mais je ne suis pas sûr de savoir comment faire le calcul. Suggestions?

Était-ce utile?

La solution

Vous connaissez le Système de numération binaire , n'est-ce pas?

Consultez en particulier ce chapitre .

EDIT: Notez également le commentaire de KFro selon lequel le quartet inférieur (= 4 bits) de la représentation binaire ASCII des nombres est en BCD. Cela rend les conversions BCD & Lt; - & Gt; ASCII très facile car il vous suffit d’ajouter / de supprimer les 4 premiers bits:

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

Autres conseils

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

Cela provient du monde des micro-contrôleurs .... Notez que les valeurs sont arrondies dans la division. Par exemple, 91 à BCD correspondrait à 91/10 * 16 = 144 + 91% 10 = 145. Converti en fichier binaire vaut 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) );
}

Habituellement, lorsque quelqu'un dit vouloir convertir un nombre décimal en caractères BCD, il parle de plusieurs chiffres décimaux.

BCD est souvent compressé dans deux chiffres décimaux par octet (car vous avez montré que 0..9 correspond à 4 bits), mais je pense qu'il est plus naturel d'utiliser un tableau d'octets, un par chiffre décimal. / p>

Un nombre binaire non signé de n bits sera inséré dans ceil (n * log_2 (10)) = ceil (n / log10 (2)) chiffres décimaux. Il tiendra également dans ceil (n / 3) = étage ((n + 2) / 3)) chiffres décimaux, puisque 2 ^ 3 = 8 est inférieur à 10.

Dans cet esprit, voici comment obtenir les chiffres décimaux d'un entier non signé:

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

Bien sûr, si vous connaissez la largeur de votre type int, vous préférerez peut-être des tableaux de longueur fixe. Il n'y a pas non plus de raison d'inverser le tout si vous vous souvenez du fait que le 0ème chiffre est le moins significatif et inversez-le uniquement en entrée / sortie. Conserver le chiffre le moins significatif en premier simplifie les opérations arithmétiques numériques dans le cas où vous n'utilisez pas un nombre fixe de chiffres.

Si vous voulez représenter " 0 " en tant que single & "; 0 &"; chiffre décimal plutôt que la chaîne de chiffre vide (l'une ou l'autre est valide), vous devez vérifier spécifiquement x == 0.

Si vous voulez deux chiffres décimaux par octet, et & "unsigned &"; est la moitié de la taille de " unsigned long " (utilisez les typedefs uint32 et uint64 si vous le souhaitez):

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

Cela vous laisse avec le chiffre décimal le moins significatif (le moins significatif) dans le demi-octet le moins significatif. Vous pouvez également exécuter la boucle un nombre fixe de fois (10 pour uint32), en ne vous arrêtant pas tôt s'il ne reste que 0 bit, ce qui permettrait à l'optimiseur de la dérouler, mais c'est plus lent si vos nombres sont souvent lents.

Est-ce que quelque chose comme ça pourrait fonctionner pour votre conversion?

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

Ce code code et décode. Les points de repère sont les suivants.

  • 45 horloges pour l'aller-retour
  • 11 horloges pour décompresser BCD dans uint32_t
  • 34 horloges pour emballer uint32_t dans BCD

J'ai utilisé un uint64_t pour stocker le BCD ici. Très pratique et largeur fixe, mais peu encombrant pour les grandes tables. Pack les chiffres BCD, 2 à char [] pour cela.

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

REMARQUE: Il semble qu'il soit impossible, même avec les inits 64 bits, de déplacer plus de 32 bits vers la gauche, mais heureusement, il est tout à fait possible de multiplier par un facteur de 16 - ce qui a heureusement l'effet recherché. C'est aussi beaucoup plus rapide. Allez comprendre.

Je sais que l'on a déjà répondu à cette question, mais je l'ai étendue pour les entiers non signés de différentes tailles en utilisant un modèle pour générer le code spécifique.

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

Voici une macro pour uint16_t, afin qu’elle soit évaluée au moment de la compilation (à condition que u soit une constante prédéfinie). Cela correspond à dec2bcd () d’en haut jusqu’à 9999.

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

Simplement simplifié.

#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);
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top