无符号整数到 BCD 的转换?

发布于 2024-08-04 14:53:01 字数 247 浏览 3 评论 0原文

我知道您可以使用此表将十进制转换为 BCD:

0 0000

1 0001

2 0010

3 0011

4 0100

5 0101

6 0110

7 0111

8 1000

9 1001

是否有此转换的方程式,或者您只需使用该表?我试图为此转换编写一些代码,但我不确定如何计算它。建议?

I know you can use this table to convert decimal to BCD:

0 0000

1 0001

2 0010

3 0011

4 0100

5 0101

6 0110

7 0111

8 1000

9 1001

Is there a equation for this conversion or you have to just use the table? Im trying to write some code for this conversion but Im not sure how to do the math for it. Suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

东风软 2024-08-11 14:53:01

您知道二进制数字系统,不是吗?

特别要看看本章

编辑:另请注意 KFro 的评论,即数字的二进制 ASCII 表示形式的低半字节(= 4 位)采用 BCD 格式。这使得 BCD <-> 转换变得容易。 ASCII 非常简单,因为您只需添加/删除前 4 位:

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

You know the Binary numeral system, don't you?

Especially have a look at this chapter.

EDIT: Also note KFro's comment that the lower nibble (= 4 bits) of the binary ASCII representation of numerals is in BCD. This makes conversions BCD <-> ASCII very easy as you just have to add/remove the leading 4 bits:

Number    ASCII Code
0         0011 0000
1         0011 0001
 ...
8         0011 1000
9         0011 1001
つ可否回来 2024-08-11 14:53:01
#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;
}
#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;
}
我是有多爱你 2024-08-11 14:53:01

这是来自微控制器世界......请注意,除法中的值是四舍五入的。例如,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) );
}

This is from the micro controller world.... Note that values are rounded in the division. For instance 91 to BCD would be 91/10 * 16 = 144 + 91%10 = 145. Converted to Binary is 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) );
}
会发光的星星闪亮亮i 2024-08-11 14:53:01

通常,当有人说他们想要从十进制转换为 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。

Usually when someone says they want to convert from decimal to BCD, they're talking about more than one decimal digit.

BCD is often packed into two decimal digits per byte (because 0..9 fit in 4 bits, as you've shown), but I think it's more natural to use an array of bytes, one per decimal digit.

An n-bit unsigned binary number will fit into ceil(n*log_2(10)) = ceil(n/log10(2)) decimal digits. It will also fit in ceil(n/3) = floor((n+2)/3)) decimal digits, since 2^3=8 is less than 10.

With that in mind, here's how I'd get the decimal digits of an unsigned 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;
}

Of course, if you know the width of your int type, you may prefer fixed length arrays. There's also no reason to reverse at all if you can remember the fact that the 0th digit is the least significant and reverse only on input/output. Keeping the least significant digit as the first simplifies digit-wise arithmetic ops in the case that you don't use a fixed number of digits.

If you want to represent "0" as the single "0" decimal digit rather than the empty digit-string (either is valid), then you'd check specifically for x==0.

各自安好 2024-08-11 14:53:01

如果您希望每个字节有两个十进制数字,并且“unsigned”是“unsigned long”大小的一半(如果需要,请使用 uint32 和 uint64 typedef):

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

这会在最低有效一半中留下最低有效(单位)十进制数字-字节。您还可以执行循环固定次数(对于 uint32 为 10)次,而不是在仅剩下 0 位时提前停止,这将允许优化器展开它,但如果您的数字通常很慢,则速度会更慢。

If you want two decimal digits per byte, and "unsigned" is half the size of "unsigned long" (use uint32 and uint64 typedefs if you want):

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

This leaves you with the least significant (unit) decimal digit in the least significant half-byte. You can also execute the loop a fixed number (10 for uint32) of times, not stopping early when only 0 bits are left, which would allow the optimizer to unroll it, but that's slower if your numbers are often slow.

想你的星星会说话 2024-08-11 14:53:01

这样的事情对你的转变有用吗?

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

Would something like this work for your 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> >();
}
錯遇了你 2024-08-11 14:53:01

该代码进行编码和解码。基准如下。

  • 往返需要 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 的某个因数 - 这很高兴地达到了预期的效果。它也快得多。去算算吧。

This code encodes and decodes. Benchmarks are as follows.

  • 45 clocks for the round-trip
  • 11 clocks for unpacking BCD to uint32_t
  • 34 clocks for packing uint32_t into BCD

I used an uint64_t to store the BCD here. Very convenient and fixed width, but not very space efficient for large tables. Pack the BCD digits, 2 to char[] for that.

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

NOTE:
It appears that it's impossible, even with 64-bit ints, to shift left more than 32 bits, but fortunately, it's entirely possible to multiply by some factor of 16 - which happily has the desired effect. It's also much faster. Go figure.

厌味 2024-08-11 14:53:01

我知道之前已经回答过这个问题,但我已经使用模板将其扩展为不同大小的无符号整数来构建特定代码。

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

I know this has been previously answered but I've extended this for unsigned ints of different sizes using a template to build the specific code.

#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;
}
围归者 2024-08-11 14:53:01

这是 uint16_t 的宏,以便在编译时对其进行评估(假设 u 是预定义的常量)。这与上面的 dec2bcd() 一致,直到 9999。

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

Here is a macro for uint16_t, so that it gets evaluated at compile-time (provided that u is a pre-defined constant). This agrees with dec2bcd() from above up to 9999.

#define U16TOBCD(u) ((((u/1000)%10)<<12)|(((u/100)%10)<<8)|\
                    (((u/10)%10)<<4)|(u%10))
一紙繁鸢 2024-08-11 14:53:01

只是简化了而已。

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

Just simplified it.

#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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文