C++ 如何将两个有符号的 8 位数字组合成一个 16 位的短数字?无法解释的结果

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8258398/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 18:16:34  来源:igfitidea点击:

C++ How to combine two signed 8 Bit numbers to a 16 Bit short? Unexplainable results

c++bytesignedshort

提问by Natalie

I need to combine two signed 8 Bit _int8 values to a signed short (16 Bit) value. It is important that the sign is not lost.

我需要将两个有符号的 8 位 _int8 值组合成一个有符号的短(16 位)值。重要的是不要丢失标志。

My code is:

我的代码是:

 unsigned short lsb = -13;
 unsigned short msb = 1;
 short combined = (msb << 8 )| lsb;

The result I get is -13. However, I expect it to be 499.

我得到的结果是-13。但是,我希望它是499.

For the following examples, I get the correct results with the same code:

对于以下示例,我使用相同的代码得到了正确的结果:

msb = -1; lsb = -6; combined = -6;
msb = 1; lsb = 89; combined = 345; 
msb = -1; lsb = 13; combined = -243;

However, msb = 1; lsb = -84; combined = -84;where I would expect 428.

但是,msb = 1; lsb = -84; combined = -84;我所期望的428.

It seems that if the lsb is negative and the msb is positive, something goes wrong! What is wrong with my code? How does the computer get to these unexpected results (Win7, 64 Bit and VS2008 C++)?

似乎如果 lsb 是负数而 msb 是正数,那就出问题了!我的代码有什么问题?计算机如何获得这些意外结果(Win7、64 Bit 和 VS2008 C++)?

回答by Retired Ninja

Your lsb in this case contains 0xfff3. When you OR it with 1 << 8 nothing changes because there is already a 1 in that bit position.

在这种情况下,您的 lsb 包含 0xfff3。当您将其与 1 << 8 进行 OR 运算时,没有任何变化,因为该位位置已经有一个 1。

Try short combined = (msb << 8 ) | (lsb & 0xff);

尝试 short combined = (msb << 8 ) | (lsb & 0xff);

回答by Ylisar

Or using a union:

或者使用联合:

#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;
}

回答by Dan

It is possible that lsbis being automatically sign-extended to 16 bits. I notice you only have a problem when it is negative and msb is positive, and that is what you would expect to happen given the way you're using the or operator. Although, you're clearly doing something very strange here. What are you actually trying to do here?

有可能lsb被自动符号扩展到 16 位。我注意到只有当它是负数而 msb 是正数时才会出现问题,这就是您使用 or 运算符的方式所期望的情况。虽然,你显然在这里做了一些非常奇怪的事情。你到底想在这里做什么?

回答by elmot

Raisonanse C complier for STM8 (and, possibly, many other compilers) generates ugly code for classic C code when writing 16-bit variables into 8-bit hardware registers. Note - STM8 is big-endian, for little-endian CPUs code must be slightly modified. Read/Write byte order is important too.

将 16 位变量写入 8 位硬件寄存器时,用于 STM8(可能还有许多其他编译器)的 Raisonanse C 编译器会为经典 C 代码生成丑陋的代码。注意 - STM8 是 big-endian,对于 little-endian CPU,代码必须稍作修改。读/写字节顺序也很重要。

So, standard C code piece:

所以,标准的 C 代码片段:

 unsigned int ch1Sum;
...
     TIM5_CCR1H = ch1Sum >> 8; 
     TIM5_CCR1L = ch1Sum; 

Is being compiled to:

正在编译为:

;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 

Too long, too slow.

太长了,太慢了。

My version:

我的版本:

     unsigned int ch1Sum;
...
     TIM5_CCR1H = ((u8*)&ch1Sum)[0];
     TIM5_CCR1L = ch1Sum;

That is compiled into adequate two MOVes

编译成足够的两个MOV

;TIM5_CCR1H = ((u8*)&ch1Sum)[0]; 
       MOV   TIM5_CCR1,ch1Sum 
;TIM5_CCR1L = ch1Sum;
       MOV   TIM5_CCR1+1,ch1Sum+1 

Opposite direction:

相反的方向:

    unsigned int uSonicRange;
...
      ((unsigned char *)&uSonicRange)[0] = TIM1_CCR2H;
      ((unsigned char *)&uSonicRange)[1] = TIM1_CCR2L;

instead of

代替

    unsigned int uSonicRange;
...
      uSonicRange = TIM1_CCR2H << 8;
      uSonicRange |= TIM1_CCR2L;

回答by tDwtp

Some things you should know about the datatypes (un)signedshortand char:

关于数据类型 ( un)签名的shortchar ,你应该知道的一些事情:

charis an 8-bit value, thats what you where looking for for lsband msb. shortis 16 bits in length.

char是一个 8 位值,这就是您要查找的lsbmsbshort 的长度为 16 位。

You should also not store signedvalues in unsignedones execpt you know what you are doing.

您也不应该将有符号值存储在无符号值中,除非您知道自己在做什么。

You can take a look at the two's complement. It describes the representation of negative values (for integers, not for floating-point values) in C/C++ and many other programming languages.

你可以看看这两个的补码。它描述了 C/C++ 和许多其他编程语言中负值(对于整数,而不是浮点值)的表示。

There are multiple versions of making your own two's complement:

制作自己的二进制补码有多种版本:

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)is more for the purpose of having exact lengths for your variable. If you really need a variable to have a specific byte-length you should use that (here you need it).

stdint.h(带有 inttypes.h)更多是为了为您的变量提供精确的长度。如果您确实需要一个变量来具有特定的字节长度,则应该使用它(在这里您需要它)。

You should everythime use datatypes which fit your needs the best. Your code should therefore look like this:

您应该每次都使用最适合您需求的数据类型。因此,您的代码应如下所示:

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

or like this:

或者像这样:

#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

For the last one the compiler will use signed 8/16-bit values everytime regardless what length inthas on your platform. Wikipediagot some nice explanation of the int8_tand int16_tdatatypes (and all the other datatypes).

对于最后一个,编译器每次都会使用有符号的 8/16 位值,而不管int在您的平台上的长度是多少。维基百科int8_tint16_t数据类型(以及所有其他数据类型)有一些很好的解释。

btw: cppreference.comis useful for looking up the ANSI Cstandards and other things that are worth to know about C/C++.

顺便说一句:cppreference.com可用于查找ANSI C标准和其他有关 C/C++ 的值得了解的内容。

回答by Jan

If this is what you want:

如果这是你想要的:

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

Use this:

用这个:

short combine(unsigned char msb, unsigned char lsb) {
    return (msb<<8u)|lsb;
}

I don't understand why you would want msb -6 and lsb -1 to generate -6 though.

我不明白为什么你想要 msb -6 和 lsb -1 生成 -6。

回答by maverik

You wrote, that you need to combine two 8-bit values. Why you're using unsigned shortthen? As Danalready said, lsbautomatically extended to 16 bits. Try the following code:

您写道,您需要组合两个 8 位值。那你为什么用unsigned short?如前所述Danlsb自动扩展到 16 位。试试下面的代码:

uint8_t lsb = -13;
uint8_t msb = 1;
int16_t combined = (msb << 8) | lsb;

This gives you the expected result: 499.

这为您提供了预期的结果:499