我需要将两个带符号的8位_int8值组合为一个带符号的短(16位)值。重要的是,不要丢失标志。
我的代码是:
unsigned short lsb = -13;
unsigned short msb = 1;
short combined = (msb << 8 )| lsb;
我得到的结果是-13
。但是,我希望它是499
。
对于以下示例,使用相同的代码可获得正确的结果:
msb = -1; lsb = -6; combined = -6;
msb = 1; lsb = 89; combined = 345;
msb = -1; lsb = 13; combined = -243;
但是,我希望在msb = 1; lsb = -84; combined = -84;
出现428
。
似乎如果lsb为负,而msb为正,则出问题了!我的代码有什么问题?计算机如何获得这些意外结果(Win7、64位和VS2008 C ++)?
在这种情况下,您的lsb包含0xfff3。当您将其与1 << 8进行或运算时,没有任何变化,因为该位位置已经有1。
尝试short combined = (msb << 8 ) | (lsb & 0xff);
或使用联合:
#include <iostream>
union Combine
{
short target;
char dest[ sizeof( short ) ];
};
int main()
{
Combine cc;
cc.dest[0] = -13, cc.dest[1] = 1;
std::cout << cc.target << std::endl;
}
[lsb
可能被自动符号扩展为16位。我注意到,只有当它为负数且msb为正数时,您才有问题,这就是您使用or运算符的方式所期望的。虽然,您显然在这里做了很奇怪的事情。您实际上想在这里做什么?
如果这是您想要的:
msb: 1, lsb: -13, combined: 499
msb: -6, lsb: -1, combined: -1281
msb: 1, lsb: 89, combined: 345
msb: -1, lsb: 13, combined: -243
msb: 1, lsb: -84, combined: 428
使用此:
short combine(unsigned char msb, unsigned char lsb) {
return (msb<<8u)|lsb;
}
我不明白为什么您会希望msb -6和lsb -1生成-6。
STM8的Raisonanse C编译器(可能还有许多其他编译器)在将16位变量写入8位硬件寄存器时,会生成经典C代码的难看代码。注意-STM8是big-endian,对于little-endian CPU,必须略微修改代码。读/写字节顺序也很重要。
因此,标准的C代码片段:
unsigned int ch1Sum;
...
TIM5_CCR1H = ch1Sum >> 8;
TIM5_CCR1L = ch1Sum;
正在被编译为:
;TIM5_CCR1H = ch1Sum >> 8;
LDW X,ch1Sum
CLR A
RRWA X,A
LD A,XL
LD TIM5_CCR1,A
;TIM5_CCR1L = ch1Sum;
MOV TIM5_CCR1+1,ch1Sum+1
太长,太慢。
我的版本:
unsigned int ch1Sum;
...
TIM5_CCR1H = ((u8*)&ch1Sum)[0];
TIM5_CCR1L = ch1Sum;
被编译成足够的两个MOV
;TIM5_CCR1H = ((u8*)&ch1Sum)[0];
MOV TIM5_CCR1,ch1Sum
;TIM5_CCR1L = ch1Sum;
MOV TIM5_CCR1+1,ch1Sum+1
相反方向:
unsigned int uSonicRange;
...
((unsigned char *)&uSonicRange)[0] = TIM1_CCR2H;
((unsigned char *)&uSonicRange)[1] = TIM1_CCR2L;
代替
unsigned int uSonicRange;
...
uSonicRange = TIM1_CCR2H << 8;
uSonicRange |= TIM1_CCR2L;
关于数据类型(un)signed short和char:
,您应该了解的一些知识”char是一个8位值,这就是您要查找lsb和msb的地方。 short的长度为16位。
您也不应在知道自己在做什么的execpt中存储signed值。您可以看一下the two's complement。它描述了C / C ++和许多其他编程语言中负值的表示(对于整数,不是浮点值)。
有多种版本来制作自己的二进制补码:
int a;
// setting a
a = -a; // Clean version. Easier to understand and read. Use this one.
a = (~a)+1; // The arithmetical version. Does the same, but takes more steps.
// Don't use the last one unless you need it!
// It can be 'optimized away' by the compiler.
stdint.h (with inttypes.h)的目的更多是为了使变量具有精确的长度。如果您确实需要一个变量来具有特定的字节长度,则应该使用该变量(在这里需要它)。
您应该每个人都使用最适合您的数据类型。因此,您的代码应如下所示:
signed char lsb; // signed 8-bit value
signed char msb; // signed 8-bit value
signed short combined = msb << 8 | (lsb & 0xFF); // signed 16-bit value
或类似这样:
#include <stdint.h>
int8_t lsb; // signed 8-bit value
int8_t msb; // signed 8-bit value
int_16_t combined = msb << 8 | (lsb & 0xFF); // signed 16-bit value
对于最后一个,无论平台上的长度[[int为何,编译器每次都会使用带符号的8/16位值。 Wikipedia对
int8_t和int16_t数据类型(以及所有其他数据类型)有了很好的解释。btw:cppreference.com可用于查找ANSI C标准和其他有关C / C ++的知识。