Cでモジュロ256演算を行う必要があります。簡単にできますか
unsigned char i;
i++;
それ以外の
int i;
i=(i+1)%256;
Cでモジュロ256演算を行う必要があります。簡単にできますか
unsigned char i;
i++;
それ以外の
int i;
i=(i+1)%256;
unsigned char
いいえ。8 ビットであることを保証するものは何もありません。uint8_t
fromを使用する<stdint.h>
と、まったく問題ありません。これには、次をサポートする実装が必要stdint.h
です。C99 準拠のコンパイラはすべてサポートしますが、古いコンパイラではサポートされない場合があります。
注: 符号なし算術演算はオーバーフローせず、「モジュロ 2^n」として動作します。未定義の動作を伴う符号付き算術オーバーフロー。
はい、両方の例の動作は同じです。C99 6.2.5 §9を参照してください。
結果の符号なし整数型で表現できない結果は、結果の型で表現できる最大値よりも 1 大きい数値を法として減らされるため、符号なしオペランドを含む計算はオーバーフローすることはありません。
おそらくそうですが、この場合の理由は実際にはかなり複雑です。
unsigned char i = 255;
i++;
はi++
と同等i = i + 1
です。
(まあ、ほぼ .は、インクリメントされる前i++
の の値を生成するので、実際には と同等です。しかし、この場合、結果は破棄されるため、追加の問題は発生しません。)i
(tmp=i; i = i + 1; tmp)
unsigned char
は狭い型であるためunsigned char
、演算子のオペランド+
は に昇格されます (の範囲内のすべての可能な値を保持できるとint
仮定します)。したがって、、およびの場合、加算の結果は であり、型は (signed)です。int
unsigned char
i == 255
UCHAR_MAX == 255
256
int
代入により、値が からに暗黙的に変換されます。unsigned 型への変換は明確に定義されています。結果は moduloで縮小されます。ここで、 はターゲットの符号なし型の最大値です。256
int
unsigned char
MAX+1
MAX
i
として宣言された場合unsigned int
:
unsigned int i = UINT_MAX;
i++;
型変換はありませんが、+
符号なし型の演算子のセマンティクスは縮小モジュールもMAX+1
指定します。
に代入される値i
は数学的に と同等であることに注意してください(i+1) % UCHAR_MAX
。UCHAR_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) == 2
int
unsigned char c = UCHAR_MAX;
c++;
基本的にはい、オーバーフローはありませんがc
、符号なしタイプのためではありません。toの隠しプロモーションとc
toint
からの整数変換が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関数」
別のデータ型を使用したくない場合は、言及されていない別の代替手段があります。
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.
オーバーフローして 0 に戻るだけなので、これは正常に機能するはずです。別の回答のコメントで指摘されているように、符号付きの値で未定義の動作が発生する可能性があるため、値が符号なしの場合にのみこれを行う必要があります。
ただし、これをモジュロのままにしておくのがおそらく最善です。なぜなら、コードを保守している他の人がコードをよりよく理解できるようになるためです。また、スマートなコンパイラがとにかくこの最適化を行っている可能性があるため、そもそも無意味になる可能性があります。その上、パフォーマンスの違いはおそらく非常に小さいため、そもそも問題にならないでしょう。
数値を表すために使用しているビット数が、除数 -1 のバイナリ (符号なし) 表現 (100000000) のビット数と等しい場合に機能します。この場合は 9-1= 8 (char) です。