我有一个问题,定义了以下马可:
TO_TI(a, b) ((b)<<13|(a))
然后我调用以下语句:
int c = TO_TI(1,2);
然后我将结果传递c
给一个函数,在这个函数中,如何根据值 c 并输入 a 计算 的值b
?
给定a
并且c
您不能(通常)计算b
,因为 a 和 b 是按位或在一起的,这是不可逆的(再次,通常)。考虑a=101
和b=1
移位为 2。 Thenc=101
与 if 相同b=0
。没有简单的办法知道。
如果您使用 XOR 而不是 OR,或者您有更多信息,例如对所涉及的值范围的限制,我们可能会取得更多进展。
将多个整数打包成一个更大的整数通常是一件有用的事情,编写宏是一种正确处理它的好方法(尽管我可能更喜欢内联函数)。
关键是你必须知道你在做什么!如果您没有正确理解底层的位操作,或者至少没有遵循规则,那么您将无法取回您输入的内容。
a
只要是使用不超过 12 位的正整数,您的 TO_TI 宏就可以正常工作。b
不能有超过 20 位的数据(假设 32 位字)。
如果a
是无符号的,它可以像这样提取:
unsigned int a = c & 0xFFF;
但是,如果a
已签名,则您必须“签名扩展”该值,如下所示:
int a = ((int)c << 20) >> 20;
同样,如果b
已签名,则必须对其进行符号扩展,但这更容易:
int b = (int)c >> 12;
但是, isb
是未签名的,您必须非常小心不要对其进行签名扩展:
unsigned int b = (unsigned int)c >> 12;
最后,如果你想允许a
有负值,那么你的宏必须像这样定义:
#define TO_TI(a, b) ((b)<<13|((a)&0xFFF))
(否则 的符号位a
将覆盖b
。)
如果您将三个或更多值编码为同一个整数,那么事情会变得更加棘手。并提防int
具有 64 位的系统。
规则:
这就是发明位域的原因,您应该强烈考虑使用它们。
如果您确定 a 除了设置前 12 位之外没有其他位(然后 | 会破坏您在 b 上的信息),您可以使用 & 并右移来反转操作:
b = (c & ~a) >> 13
(~a 是 a 的按位否定)
编辑:实际上甚至不需要执行 & 因为通过右移最低 12 位将被移出......
该宏TO_TI
遵循一个习惯用法,将多个值打包到位域中。(它也在对你眨眼。)
b << 13
乘以b
2 13 = 8192。只要a
最多 13 位长,即0 <= a < 8192
,您可以通过取 8192 的模数来恢复它。您不需要b
恢复a
……反之亦然。
整个想法是a
并且b
应该很容易从 的结果中恢复TO_TI
,因此首先要查看该宏定义周围的源代码,以找到应该反转该过程的宏。否则,c >> 13
应该足以恢复b
。
应该注意的是,这两个数字都必须是正数,并且在所需的范围内才能正常工作。如果作者TO_TI
从来没有提到a
不能大于 8192,他们在方向盘上睡着了。
如果我正确理解了这个问题,给出的c来自
c = b<<13 | a;
我们能找出原来的 a和b吗?
这取决于
a的长度(位数):除上述问题外,如果a msb(最高有效位)为 2^13 或更大,则该|
操作将影响b在移位 ( b << 13
) 之后的位置,因此您无法知道第 13 位的c值来自原始b或来自a
b的长度:移位 13 位是将b乘以2^13 (8192),如果b本来就足够大,则移位后它可能会溢出c,即结果不适合c大小(长度),并且从下面的方法中检索到的b将小于原始b。
因此,如果 a小于 8192 (2^13) 但为正(否则它的 msb 将达到b移位)并且 b左移 13 位不会溢出c您可以检索a和b,使用
a = c & 8191; // 2^13 - 1 or 0x1fff in hexa
b = c >> 13;