我知道您可以使用此表将十进制转换为 BCD:
0 0000
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110
7 0111
8 1000
9 1001
这种转换是否有方程式,或者您必须使用表格?我试图为这种转换编写一些代码,但我不确定如何计算它。建议?
#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;
}
这是来自微控制器世界....请注意,除法中的值是四舍五入的。例如 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) );
}
通常当有人说他们想从十进制转换为 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。
考虑到这一点,这就是我如何获得无符号整数的十进制数字:
#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。
如果您希望每个字节有两个十进制数字,并且“无符号”是“无符号长”大小的一半(如果需要,请使用 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;
}
这会在最低有效半字节中留下最低有效(单位)十进制数字。您还可以执行循环固定次数(对于 uint32 为 10),当只剩下 0 位时不会提前停止,这将允许优化器展开它,但如果您的数字通常很慢,那会更慢。
这样的事情对您的转换有用吗?
#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> >();
}
此代码进行编码和解码。基准如下。
我在这里使用了一个 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 的某个因子——这很高兴达到了预期的效果。它也快得多。去搞清楚。
我知道这之前已经回答过了,但我已经使用模板将其扩展为不同大小的无符号整数来构建特定代码。
#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;
}
这是 uint16_t 的宏,以便在编译时对其进行评估(前提是 u 是预定义的常量)。这与 dec2bcd() 从上面到 9999 一致。
#define U16TOBCD(u) ((((u/1000)%10)<<12)|(((u/100)%10)<<8)|\
(((u/10)%10)<<4)|(u%10))
只是简化了。
#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);
}