背景
之前跟FPGA同事讨论版本号的编码时,为了方便在16进制下查看,决定选择BCD编码,效果大概这样:
0x25121801
25表示25年,12表示12月,18表示18号,01表示当日的第一版,很方便,但是上位机对版本号的要求是像IP地址那样的编码,所以上面的那串BCD数字就要转换成
0x190c1201
19表示十六进制的25年,0c表示十六进制的12月,12表示十六进制的18号,01表示十六进制的当日第一版
思路
BCD是用4bit编码一个十进制数位,而IP地址其实就是用8bit编码一个字段,既然2个BCD数位对应一个版本号的字段,因此可以简单的2:1转换:

为了避免移位运算,考虑用联合体来实现既能直接操作每个8bit,又能将转换结果按32bit整型返回的效果,而且还不用管CPU到底是大端还是小端。
C语言代码
c
#include <stdio.h>
#include <stdint.h>
union bcd {
uint8_t field[4];
uint32_t reg;
};
uint32_t bcd_to_hex(uint32_t reg)
{
union bcd d;
d.reg = reg;
for (int i = 0; i < 4; i++) {
d.field[i] = (d.field[i] >> 4) * 10 + (d.field[i] & 0xf);
}
return d.reg;
}
int main()
{
uint32_t reg = 0x25121801;
printf("ori 0x%08x\n", reg);
printf("convert 0x%08x\n", bcd_to_hex(reg));
return 0;
}
运行效果:
ori 0x25121801
convert 0x190c1201
后记
可惜C语言没有内置niche类型,否则联合体里再定义个niche_t sub_feild[8];,上述代码就不用做任何位运算了。