web-dev-qa-db-ja.com

C ++ 2つの符号付き8ビット数を16ビットショートに組み合わせる方法は?説明できない結果

2つの符号付き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++)に到達しますか?

11
Natalie

この場合のlsbには0xfff3が含まれています。 OR 1 << 8の場合、そのビット位置にはすでに1があるため、何も変わりません。

short combined = (msb << 8 ) | (lsb & 0xff);を試してください

22
Retired Ninja

または和集合を使用する:

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

lsbが自動的に16ビットに符号拡張されている可能性があります。負の場合とmsbが正の場合にのみ問題が発生することに気付きました。これは、or演算子の使用方法を考えると予想されることです。しかし、あなたは明らかにここで非常に奇妙なことをしているのです。ここで実際に何をしようとしていますか?

2
Dan

これが必要な場合:

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を生成する理由がわかりません。

1
Jan

STM8用のRaisonanseCコンパイラー(および場合によっては他の多くのコンパイラー)は、16ビット変数を8ビットハードウェアレジスターに書き込むときに、従来のCコード用の醜いコードを生成します。注-STM8はビッグエンディアンです。リトルエンディアンの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;

それは適切な2つの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;
1
elmot

データ型について知っておくべきいくつかのこと(nsignedshortおよびchar

charは8ビット値であり、これを探している場所ですlsbおよびmsbshortの長さは16ビットです。

また、signed値をnsigned実行している値に格納しないでください。

2の補数 を見てください。 C/C++および他の多くのプログラミング言語での負の値(浮動小数点値ではなく整数の場合)の表現について説明します。

独自の2の補数を作成する方法は複数あります。

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

最後の1つでは、コンパイラは、プラットフォームの長さに関係なく、常に符号付き8/16ビット値を使用しますintWikipediaint8_tおよびint16_tデータ型(および他のすべてのデータ型)についてのわかりやすい説明があります。

ところで: cppreference.com は、 ANSI C 標準やC/C++について知っておく価値のあるその他のことを調べるのに役立ちます。

1
tDwtp

あなたは、2つの8ビット値を組み合わせる必要があると書きました。では、なぜunsigned shortを使用しているのですか? Danがすでに述べたように、lsbは自動的に16ビットに拡張されます。次のコードを試してください。

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

これにより、期待される結果が得られます:499

0
maverik