C言語:#defineで定義値は、最大8ビットの乗算を台無し。どうして?
-
16-09-2019 - |
質問
私は、次のCコードを持っています:
#define PRR_SCALE 255
...
uint8_t a = 3;
uint8_t b = 4;
uint8_t prr;
prr = (PRR_SCALE * a) / b;
printf("prr: %u\n", prr);
私はコンチキすると呼ばれる小さな組み込みOSのため、MSP430プラットフォームコンパイラを使用して(これをコンパイルする場合私は191を期待しながら)結果は0です。 (uint8_tは、unsigned char型としてtypedefされている)
私はそれを変更する場合:
uint8_t a = 3;
uint8_t b = 4;
uint8_t c = 255;
uint8_t prr;
prr = (c * a) / b;
printf("prr: %u\n", prr);
それが正常にうまくいくと191を出力します。
この単純なバージョンをコンパイルする「通常」のUbuntuボックスのGCCを使用すると、両方の場合において正しい値を出力します。
私はこれである理由を正確にわかりません。私は、事前に変数に定義された値を割り当てることによって、それを回避することもできますが、私はむしろそれをしないと思います。
これは、なぜ誰もが知っていますか?おそらく、これについていくつかのより多くの情報へのリンクと?
解決
短い答え:あなたのコンパイラにバグがあります。 (他の人が示唆したように、オーバーフローには問題ありません。)
いずれの場合においても、演算は、長い、少なくとも16ビットであることが保証され、int
で行われます。 255
がint
であるため、元のスニペットでは、後者では、それが原因で不可欠なプロモーションのだ、です>。
あなたが述べたように、gccはこれを正しく処理します。
他のヒント
255は、整数リテラルとして処理され、式全体がintベースではなくunsigned char型基づいさせるされています。後者の場合は、正しいとタイプを強制します。次のようにあなたの#defineを変更してみてください。
#define PRR_SCALE ((uint8_t) 255)
は、バイナリ/ HEXファイルと一緒にコンパイルされたプログラムのアセンブラリストを出す必要があります。他のコンパイラは、そうするために追加のコンパイラフラグが必要な場合があります。または多分別の逆アセンブラは、バイナリで実行します。
これは説明を探すための場所です。 コンパイラの最適化に、プロセッサに提示される実際のコードは、元のCコードにあまり類似性を有する(しかし、通常は、同じ仕事をしていません)があります。
問題の原因を明らかにする必要があり、故障コードを表すいくつかのアセンブラ命令のステップ実行ます。
私の推測では、定義された定数は、コンパイル時に知られて一部であるSICEコンパイラは何とか全体の計算を最適化していることです。 (迅速かつ小さい)255 Xに最適化することができる* X << 8、X たぶん何かが最適化されたアセンブラコードで間違ってされます。
私は自分のシステムに両方のバージョンをコンパイルする時間がかかりました。アクティブな最適化では、mspgccは、次のコードを生成します:
#define PRR_SCALE 255
uint8_t a = 3;
uint8_t b = 4;
uint8_t prr;
prr = (PRR_SCALE * a) / b;
40ce: 3c 40 fd ff mov #-3, r12 ;#0xfffd
40d2: 2a 42 mov #4, r10 ;r2 As==10
40d4: b0 12 fa 6f call __divmodhi4 ;#0x6ffa
40d8: 0f 4c mov r12, r15 ;
printf("prr: %u\n", prr);
40da: 7f f3 and.b #-1, r15 ;r3 As==11
40dc: 0f 12 push r15 ;
40de: 30 12 c0 40 push #16576 ;#0x40c0
40e2: b0 12 9c 67 call printf ;#0x679c
40e6: 21 52 add #4, r1 ;r2 As==10
私たちが見ることができるように、コンパイラは直接-3(0xfffd)に255 * 3の結果を計算します。そして、ここでの問題はあります。 255符号なし16ビットの8ビットの代わりに署名されたように-1何とか255は解釈されます。あるいは、最初の8ビットに解析され、その後、16ビットに符号拡張されています。またはどんなます。
このトピックに関する議論はすでにmspgccメーリングリストで開始されています。
インクルードが動作しない定義、なぜ私はわからないんだけど、あなたはuint8_t
変数とロールオーバーに実行している可能性があります。 255はuint8_t (2^8 - 1)
の最大値であるので、あなたは3で、あなたはいくつかの微妙なロールオーバー問題に実行するためにバインドされていることを掛けた場合ます。
コンパイラがコードを最適化するかもしれない、とあなたの数学の式の結果を事前に計算し、(それがフィットするので、中間値が収まらないにもかかわらず)PRRで結果を無理に勧めています。
(これはあなたが望むもののように動作しません)あなたはこのようなあなたの表現を破るとどうなるか確認します:
prr = c * a; // rollover!
prr = prr / b;
あなただけの大きなデータ型を使用する必要があります。
私は考えることができる一つの違いケース-1は、
PRR_SCALEリテラル値は、ROMまたはコード領域に入ることができます。そして、言うのためのMULのオペコードでいくつかの違いがあるかもしれない、
case-1: [register], [rom]
case -2: [register], [register]
これは全く意味を成さないことがあります。