C++ 无符号整数到 BCD 的转换?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1408361/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Unsigned Integer to BCD conversion?
提问by Bramble
I know you can use this table to convert decimal to BCD:
我知道您可以使用此表将十进制转换为 BCD:
0 0000
0 0000
1 0001
1 0001
2 0010
2 0010
3 0011
3 0011
4 0100
4 0100
5 0101
5 0101
6 0110
6 0110
7 0111
7 0111
8 1000
8 1000
9 1001
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?
这种转换是否有等式,或者您必须使用表格?我正在尝试为此转换编写一些代码,但我不确定如何为它做数学运算。建议?
回答by schnaader
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:
编辑:另请注意 KFro 的评论,即数字的二进制 ASCII 表示的低半字节(= 4 位)在 BCD 中。这使得转换 BCD <-> ASCII 非常容易,因为您只需要添加/删除前导 4 位:
Number ASCII Code 0 0011 0000 1 0011 0001 ... 8 0011 1000 9 0011 1001
回答by Simon Peverett
#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;
}
回答by nmushov
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.
这是来自微控制器世界....请注意,值在除法中四舍五入。例如 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) );
}
回答by Jonathan Graehl
Usually when someone says they want to convert from decimal to BCD, they're talking about more than one decimal digit.
通常当有人说他们想从十进制转换为 BCD 时,他们谈论的不是一位十进制数字。
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.
BCD 通常每字节打包成两个十进制数字(因为 0..9 适合 4 位,如您所示),但我认为使用字节数组更自然,每个十进制数字一个。
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.
n 位无符号二进制数将适合 ceil(n*log_2(10)) = ceil(n/log10(2)) 十进制数字。它也适合 ceil(n/3) = floor((n+2)/3)) 十进制数字,因为 2^3=8 小于 10。
With that in mind, here's how I'd get the decimal digits of an unsigned int:
考虑到这一点,这里是我如何获得 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.
当然,如果您知道 int 类型的宽度,您可能更喜欢定长数组。如果您还记得第 0 位数字是最不重要的并且仅在输入/输出时反转这一事实,则根本没有理由反转。在您不使用固定位数的情况下,将最低有效数字保留为第一个可以简化逐位算术运算。
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.
如果您想将“0”表示为单个“0”十进制数字而不是空数字字符串(任一有效),那么您将专门检查 x==0。
回答by Jonathan Graehl
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):
如果您希望每个字节有两个十进制数字,并且“无符号”是“无符号长”的一半(如果需要,请使用 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;
}
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.
这为您留下了最低有效半字节中最低有效(单位)十进制数字。您还可以执行循环固定次数(对于 uint32 为 10)次,而不是在只剩下 0 位时提前停止,这将允许优化器展开它,但如果您的数字通常很慢,则速度会更慢。
回答by CTT
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> >();
}
回答by CTT
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
- 往返45个时钟
- 11 个时钟用于将 BCD 解包到 uint32_t
- 将 uint32_t 打包成 BCD 的 34 个时钟
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 来存储 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;
}
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.
注意:即使使用 64 位整数,左移超过 32 位似乎也是不可能的,但幸运的是,完全有可能乘以 16 的某个因数——这很高兴地达到了预期的效果。它也快得多。去搞清楚。
回答by Andrew Stern
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;
}
回答by Ray Foulk
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.
这是 uint16_t 的宏,以便在编译时对其进行评估(前提是 u 是预定义的常量)。这与 dec2bcd() 从上面到 9999 一致。
#define U16TOBCD(u) ((((u/1000)%10)<<12)|(((u/100)%10)<<8)|\
(((u/10)%10)<<4)|(u%10))
回答by randz
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);
}