25

Cでモジュロ256演算を行う必要があります。簡単にできますか

unsigned char i;
i++;

それ以外の

int i;
i=(i+1)%256;
4

7 に答える 7

25

unsigned charいいえ。8 ビットであることを保証するものは何もありません。uint8_tfromを使用する<stdint.h>と、まったく問題ありません。これには、次をサポートする実装が必要stdint.hです。C99 準拠のコンパイラはすべてサポートしますが、古いコンパイラではサポートされない場合があります。

注: 符号なし算術演算はオーバーフローせず、「モジュロ 2^n」として動作します。未定義の動作を伴う符号付き算術オーバーフロー。

于 2014-02-06T18:44:22.477 に答える
6

はい、両方の例の動作は同じです。C99 6.2.5 §9を参照してください。

結果の符号なし整数型で表現できない結果は、結果の型で表現できる最大値よりも 1 大きい数値を法として減らされるため、符号なしオペランドを含む計算はオーバーフローすることはありません。

于 2014-02-06T18:42:22.327 に答える
5

おそらくそうですが、この場合の理由は実際にはかなり複雑です。

unsigned char i = 255;
i++;

i++と同等i = i + 1です。

(まあ、ほぼ .は、インクリメントされる前i++の の値を生成するので、実際には と同等です。しかし、この場合、結果は破棄されるため、追加の問題は発生しません。)i (tmp=i; i = i + 1; tmp)

unsigned charは狭い型であるためunsigned char、演算子のオペランド+は に昇格されます (の範囲内のすべての可能な値を保持できるとint仮定します)。したがって、、およびの場合、加算の結果は であり、型は (signed)です。intunsigned chari == 255UCHAR_MAX == 255256int

代入により、値が からに暗黙的に変換されます。unsigned 型への変換は明確に定義されています。結果は moduloで縮小されます。ここで、 はターゲットの符号なし型の最大値です。256intunsigned charMAX+1MAX

iとして宣言された場合unsigned int:

unsigned int i = UINT_MAX;
i++;

型変換はありませんが、+符号なし型の演算子のセマンティクスは縮小モジュールMAX+1指定します。

に代入される値iは数学的に と同等であることに注意してください(i+1) % UCHAR_MAXUCHAR_MAX通常であり 、少なくとも255であることが保証されていますが、合法的にはそれより大きくなる可能性があります。 255

UCHAR_MAX署名されたオブジェクトに格納するにはあまりにも多くのエキゾチックなシステムが存在する可能性がありintます。これには が必要ですUCHAR_MAX > INT_MAX。つまり、システムには少なくとも16 ビットのバイトが必要です。このようなシステムでは、昇格は からunsigned charまでになりunsigned intます。最終結果は同じになります。そのようなシステムに遭遇する可能性は低いです。8 ビットより大きいバイトを持つ一部のDSPには C 実装があると思います。バイトのビット数はCHAR_BIT、 で定義された で指定され<limits.h>ます。

CHAR_BIT > 8必ずしも意味するものではありませんUCHAR_MAX > INT_MAX。たとえば、16 ビットのバイトと 32 ビットの s などがCHAR_BIT == 16あります)。sizeof (int) == 2int

于 2014-02-06T18:46:03.907 に答える
5
unsigned char c = UCHAR_MAX;
c++;

基本的にはい、オーバーフローはありませんがc、符号なしタイプのためではありません。toの隠しプロモーションとctointからの整数変換がintありunsigned char、完全に定義されています。

例えば、

 signed char c = SCHAR_MAX;
 c++;

実際には次と同等であるため、未定義の動作でもありません。

c = (int) c + 1;

intからへの変換signed charは実装定義です (整数変換については c99、6.3.1.3p3 を参照してください)。簡略化するCHAR_BIT == 8ことを前提としています。

上記の例の詳細については、次の投稿を読むことをお勧めします。

「地獄のリトルC関数」

http://blog.regehr.org/archives/482

于 2014-02-06T18:47:49.803 に答える
3

別のデータ型を使用したくない場合は、言及されていない別の代替手段があります。

unsigned int i;
// ...
i = (i+1) & 0xFF; // 0xFF == 255

これは、モジュロ要素 ==2^nであるため機能します。つまり、範囲は に[0, 2^n-1]なり、ビットマスクは値を目的の範囲内に簡単に維持できます。このメソッドは、コンパイラが舞台裏で行う魔法と、対象となるシステムが非単語ロードを処理する方法によっては、 unsigned char/バージョンよりも効率的ではないか、またはそれほど効率的ではない可能性があります (たとえば、一部の RISC アーキテクチャでは、uint8_tワードサイズ以外の値をロードします)。これはまた、コンパイラが符号なし値に対する 2 のべき乗モジュロ算術の使用を検出せず、もちろん、モジュロの使用がより大きな意味値を持つような場合のように、ビットマスクを代用しないことを前提としています (ただし、それを使用します)。もちろん、あなたの決定の根拠は正確に移植可能ではないためです)。

この方法の利点は、データ型のサイズではない 2 のべき乗に使用できることです。

i = (i+1) & 0x1FF; // i %= 512
i = (i+1) & 0x3FF; // i %= 1024
// etc.
于 2014-02-06T19:38:54.463 に答える
2

オーバーフローして 0 に戻るだけなので、これは正常に機能するはずです。別の回答のコメントで指摘されているように、符号付きの値で未定義の動作が発生する可能性があるため、値が符号なしの場合にのみこれを行う必要があります。

ただし、これをモジュロのままにしておくのがおそらく最善です。なぜなら、コードを保守している他の人がコードをよりよく理解できるようになるためです。また、スマートなコンパイラがとにかくこの最適化を行っている可能性があるため、そもそも無意味になる可能性があります。その上、パフォーマンスの違いはおそらく非常に小さいため、そもそも問題にならないでしょう。

于 2014-02-06T18:38:26.540 に答える
1

数値を表すために使用しているビット数が、除数 -1 のバイナリ (符号なし) 表現 (100000000) のビット数と等しい場合に機能します。この場合は 9-1= 8 (char) です。

于 2014-02-06T18:40:39.237 に答える