【问题标题】:C++ How to combine two signed 8 Bit numbers to a 16 Bit short? Unexplainable resultsC ++如何将两个带符号的8位数字组合成一个16位短?无法解释的结果
【发布时间】:2012-01-05 16:50:38
【问题描述】:

我需要将两个带符号的 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++)?

【问题讨论】:

标签: c++ byte signed short


【解决方案1】:

在这种情况下,您的 lsb 包含 0xfff3。当您将其与 1

试试short combined = (msb &lt;&lt; 8 ) | (lsb &amp; 0xff);

【讨论】:

    【解决方案2】:

    或者使用联合:

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

    【讨论】:

    • 由于可移植性问题,不建议使用联合。
    【解决方案3】:

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

    【讨论】:

    • 谢谢丹!我读入的文件中的数据存储为 8Bit 有符号整数,始终先是 LSB,然后是 MSB。要使用这些数据,我需要将它们组合成一个 16 位有符号短。我以 char 形式读取文件并转换为 _int8。如何正确地做到这一点?
    • @Natalie:恐怕我不认为你说的有道理。多字节整数表示的各个字节如何甚至具有“符号”的概念?你为什么不把这两个字节读成一个有符号的短字节然后完成呢?
    • 对不起,我没有发明这种文件格式。我必须将整个文件作为 char 读取,因为该文件还包含 ASCII 部分,并且并非文件中的所有数据都必须组合成短裤。我需要首先分离数据以获取存储为 8 位签名 int 的数据。
    【解决方案4】:

    在将 16 位变量写入 8 位硬件寄存器时,适用于 STM8 的 Raisonanse C 编译器(可能还有许多其他编译器)会为经典 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;
    

    编译成足够的两个MOVes

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

    【讨论】:

      【解决方案5】:

      您写道,您需要组合两个 8 位值。那你为什么要使用unsigned short? 正如Dan 已经说过的,lsb 自动扩展为 16 位。试试下面的代码:

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

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

      【讨论】:

      • 我尝试使用 uint8_t 但我的编译器不知道这种类型,并且 #include 不会导致此类文件或目录错误。也许是VS2008的问题?谢谢!
      • 对于 VS 使用 unsigned __int8__int16 类型。
      【解决方案6】:

      如果这是你想要的:

      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。

      【讨论】:

      • 你是对的!对不起,我在这个例子中混合了 msb 和 lsb 并在我的帖子中更正了它。谢谢!
      【解决方案7】:

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

      char 是一个 8 位的值,这就是你在寻找 lsbmsb 的地方。 short 长度为 16 位。

      您也不应该将 signed 值存储在 unsigned 值中,除非您知道自己在做什么。

      你可以看看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
      

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-01-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多