Преобразование целого числа в BCD без знака? - PullRequest
7 голосов
/ 11 сентября 2009

Я знаю, что вы можете использовать эту таблицу для преобразования десятичного числа в BCD:

0 0000

1 0001

2 0010

3 0011

4 0100

5 0101

6 0110

7 0111

8 1000

9 1001

Есть ли уравнение для этого преобразования, или вам нужно просто использовать таблицу? Я пытаюсь написать код для этого преобразования, но я не уверен, как сделать математику для него. Предложения? * * 1023

Ответы [ 10 ]

8 голосов
/ 11 сентября 2009

Вы знаете двоичную систему счисления , не так ли?

Особенно взгляните на эту главу .

РЕДАКТИРОВАТЬ: Обратите также внимание на комментарий KFro, что нижний полубайт (= 4 бита) двоичного ASCII-представления чисел находится в BCD. Это делает преобразование BCD <-> ASCII очень простым, так как вам просто нужно добавить / удалить первые 4 бита:

Number    ASCII Code
0         0011 0000
1         0011 0001
 ...
8         0011 1000
9         0011 1001
7 голосов
/ 14 января 2014
#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;
}
6 голосов
/ 07 февраля 2012

Это из мира микроконтроллеров .... Обратите внимание, что значения округлены в делении. Например, 91 для BCD будет 91/10 * 16 = 144 + 91% 10 = 145. Преобразован в двоичный код - 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) );
}
5 голосов
/ 11 сентября 2009

Обычно, когда кто-то говорит, что хочет преобразовать десятичную в BCD, он говорит о более чем одной десятичной цифре.

BCD часто упаковывается в две десятичные цифры на байт (потому что 0..9 помещается в 4 бита, как вы показали), но я думаю, что более естественно использовать массив байтов, по одному на десятичную цифру.

n-разрядное двоичное число без знака будет помещаться в десятичные цифры ceil (n * log_2 (10)) = ceil (n / log10 (2)). Он также будет соответствовать десятичным цифрам ceil (n / 3) = floor ((n + 2) / 3)), так как 2 ^ 3 = 8 меньше 10.

Имея это в виду, вот как я могу получить десятичные цифры без знака int:

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

Конечно, если вы знаете ширину вашего типа int, вы можете предпочесть массивы фиксированной длины. Также нет никакой причины полностью изменить, если вы помните тот факт, что 0-ая цифра является наименее значимой, и наоборот только на входе / выходе. Сохранение наименее значащей цифры в качестве первой упрощает арифметические операции с цифрами в случае, если вы не используете фиксированное количество цифр.

Если вы хотите представить «0» как одну десятичную цифру «0», а не пустую цифру-строку (любая из них действительна), то вам следует специально проверить x == 0.

1 голос
/ 11 сентября 2009

Если вам нужны две десятичные цифры на байт, а значение unsigned равно половине размера long без знака (используйте, если хотите, значения типа uint32 и uint64):

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

Это оставляет вас с младшей значащей (единичной) десятичной цифрой в младшем значащем полубайте. Вы также можете выполнить цикл фиксированное число раз (10 для uint32), не останавливаясь раньше, когда останется только 0 битов, что позволит оптимизатору развернуть его, но это медленнее, если ваши числа часто бывают медленными.

0 голосов
/ 06 ноября 2017

Просто упростил.

#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);
}
0 голосов
/ 07 августа 2017

Вот макрос для uint16_t, так что он вычисляется во время компиляции (при условии, что u является предварительно определенной константой). Это согласуется с dec2bcd () сверху до 9999.

#define U16TOBCD(u) ((((u/1000)%10)<<12)|(((u/100)%10)<<8)|\
                    (((u/10)%10)<<4)|(u%10))
0 голосов
/ 11 января 2017

Я знаю, что на этот вопрос уже был дан ответ, но я расширил его для неподписанных целочисленных объектов различных размеров, используя шаблон для создания определенного кода.

#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 :
//  /914657/preobrazovanie-tselogo-chisla-v-bcd-bez-znaka
//  &&
//  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;
}
0 голосов
/ 08 ноября 2014

Этот код кодирует и декодирует. Тесты следующие.

  • 45 часов в оба конца
  • 11 часов для распаковки BCD в uint32_t
  • 34 часа для упаковки uint32_t в BCD

Я использовал uint64_t для хранения BCD здесь. Очень удобная и фиксированная ширина, но не очень компактная для больших столов. Для этого упакуйте цифры BCD, 2 в char [].

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

Примечание: Похоже, что невозможно, даже с 64-битными целочисленными значениями, сдвигать влево более чем на 32 бита, но, к счастью, вполне возможно умножить на коэффициент 16, что, к счастью, дает желаемый эффект. Это также намного быстрее. Пойди разберись.

0 голосов
/ 11 сентября 2009

Хотелось бы что-нибудь подобное для вашей конверсии?

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