我有一个 8 位 BCD 号码,需要检查它是否是有效的 BCD 号码。我怎样才能以编程方式(C/C++)做到这一点?
例如:0x12345678 有效,但 0x00f00abc 无效。
提前致谢!
您需要检查每个 4 位数量以确保它小于 10。为了提高效率,您希望一次处理尽可能多的位。
在这里,我将数字分开以在每个数字之间留下一个零,然后将每个数字加 6 并检查是否溢出。
uint32_t highs = (value & 0xf0f0f0f0) >> 4;
uint32_t lows = value & 0x0f0f0f0f;
bool invalid = (((highs + 0x06060606) | (lows + 0x06060606)) & 0xf0f0f0f0) != 0;
编辑:实际上我们可以做得更好。检测溢出不需要 4 位,只需 1。如果我们将所有数字除以 2,它会释放一点,我们可以一次检查所有数字。
uint32_t halfdigits = (value >> 1) & 0x77777777;
bool invalid = ((halfdigits + 0x33333333) & 0x88888888) != 0;
对于无效的数字,它需要是 10-15。这反过来意味着 8 + 4 或 8+2 - 低位根本不重要。
所以:
long mask8 = value & 0x88888888;
long mask4 = value & 0x44444444;
long mask2 = value & 0x22222222;
return ((mask8 >> 2) & ((mask4 >>1) | mask2) == 0;
不太明显:
long mask8 = (value>>2);
long mask42 = (value | (value>>1);
return (mask8 & mask42 & 0x22222222) == 0;
通过在遮罩之前移动,我们不需要 3 个不同的遮罩。
显而易见的方法是:
/* returns 1 if x is valid BCD */
int
isvalidbcd (uint32_t x)
{
for (; x; x = x>>4)
{
if ((x & 0xf) >= 0xa)
return 0;
}
return 1;
}
这个链接告诉你所有关于 BCD 的信息,并推荐这样的东西作为更优化的解决方案(重新检查所有数字,因此使用 64 位数据类型,未经测试):
/* returns 1 if x is valid BCD */
int
isvalidbcd (uint32_t x)
{
return !!(((uint64_t)x + 0x66666666ULL) ^ (uint64_t)x) & 0x111111110ULL;
}
灵感来自@Mark Ransom
bool invalid = (0x88888888 & (((value & 0xEEEEEEEE) >> 1) + (0x66666666 >> 1))) != 0;
// or
bool valid = !((((value & 0xEEEEEEEEu) >> 1) + 0x33333333) & 0x88888888);
屏蔽每个 BCD 数字的 1 的位置,右移,然后加 6 并检查 BCD 数字溢出。
这是如何工作的:
通过将 +6 添加到每个数字,我们寻找*
4 位和的溢出。
abcd
+ 110
-----
*efgd
但是 的位值对d
总和没有贡献,因此首先屏蔽该位并右移。现在溢出位8's
就位了。这一切都是并行完成的,我们用这些进位位屏蔽0x88888888
并测试是否设置了任何位。
0abc
+ 11
-----
*efg