衒学的なプログラマーは、標準で保証されていない仮定をせずに、これを防ぐにはどうすればよいでしょうか?
1 つの方法は、符号なし整数を使用することです。符号なし整数のオーバーフロー動作は、符号付き整数から符号なし整数に変換するときの動作と同様に明確に定義されています。
したがって、以下は安全であると思います(一部の本当にあいまいなシステムでは恐ろしく壊れていることが判明しました。改善されたバージョンについては、投稿の後半を参照してください)
uintmax_t j = i;
if (j > (uintmax_t)INTMAX_MAX) {
j = -j;
}
printf("Result: |%jd| = %ju\n", i, j);
では、これはどのように機能するのでしょうか。
uintmax_t j = i;
これにより、符号付き整数が符号なし整数に変換されます。正の場合、値は同じままです。負の場合、値は 2 n増加します(n はビット数)。これにより、大きな数 (INTMAX_MAX より大きい) に変換されます。
if (j > (uintmax_t)INTMAX_MAX) {
元の数値が正の場合 (したがって INTMAX_MAX 以下の場合)、これは何もしません。元の数値が負の場合、if ブロックの内部が実行されます。
j = -j;
番号は否定されます。否定の結果は明らかに負であるため、符号なし整数として表すことはできません。したがって、2 n増加します。
したがって、代数的に負の i の結果は次のようになります
j = - (i + 2 n ) + 2 n = -i
賢いですが、このソリューションは仮定をしています。これは、C 標準で許可されている INTMAX_MAX == UINTMAX_MAX の場合に失敗します。
うーん、これを見てみましょう ( https://busybox.net/~landley/c99-draft.htmlを読んでいますが、これは明らかに標準化前の最後の C99 ドラフトです。最終的な標準で何か変更があった場合は教えてください。
最初の u の有無のみが異なる typedef 名が定義されている場合、それらは 6.2.5 で説明されているように、対応する符号付きおよび符号なしの型を示すものとします。実装は、対応する型も提供せずに型を提供してはなりません。
6.2.5でわかります
符号付き整数型ごとに、対応する (ただし異なる) 符号なし整数型 (キーワード unsigned で指定) があり、同じ量のストレージ (符号情報を含む) を使用し、同じ配置要件があります。
6.2.6.2でわかりました
#1
unsigned char 以外の unsigned 整数型の場合、オブジェクト表現のビットは、値ビットとパディング ビットの 2 つのグループに分けられます (後者のいずれかが存在する必要はありません)。N 個の値のビットがある場合、各ビットは 1 から 2N-1 までの異なる 2 のべき乗を表し、その型のオブジェクトは純粋な 2 進数表現を使用して 0 から 2N-1 までの値を表すことができなければならない。これは、値表現として知られるものとします。パディングビットの値は規定されていない.39)
#2
符号付き整数型の場合、オブジェクト表現のビットは、値ビット、パディング ビット、符号ビットの 3 つのグループに分けられます。パディング ビットは必要ありません。正確に 1 つの符号ビットがあります。値ビットである各ビットは、対応する符号なし型のオブジェクト表現の同じビットと同じ値を持つものとします (符号付き型に M 個の値ビットがあり、符号なし型に N 個の値ビットがある場合、M<=N)。符号ビットがゼロの場合、結果の値には影響しません。
そうです、あなたは正しいようですが、符号付きと符号なしの型は同じサイズでなければなりませんが、符号なしの型が符号付きの型よりも1つ多いパディングビットを持つことは有効であるようです。
わかりました、上記の分析に基づいて、最初の試行で欠陥を明らかにしたので、より偏執的なバリアントを作成しました。これには、最初のバージョンから 2 つの変更点があります。
j > (uintmax_t)INTMAX_MAX ではなく i < 0 を使用して、負の数をチェックします。これは、INTMAX_MAX == UINTMAX_MAX の場合でも、アルゴリズムが -INTMAX_MAX 以上の数値に対して正しい結果を生成することを意味します。
INTMAX_MAX == UINTMAX_MAX、INTMAX_MIN == -INTMAX_MAX -1、i == INTMAX_MIN のエラー ケースの処理を追加します。これにより、簡単にテストできる if 条件内で j=0 が発生します。
C 標準の要件から、INTMAX_MIN を -INTMAX_MAX -1 より小さくすることはできないことがわかります。これは、符号ビットが 1 つしかなく、値のビット数が対応する符号なしの型と同じかそれ以下でなければならないためです。小さい数値を表すためのビット パターンはまったく残っていません。
uintmax_t j = i;
if (i < 0) {
j = -j;
if (j == 0) {
printf("your platform sucks\n");
exit(1);
}
}
printf("Result: |%jd| = %ju\n", i, j);
@plugwash 2501が正しいと思います。たとえば、-UINTMAX_MAX 値は 1: (-UINTMAX_MAX + (UINTMAX_MAX + 1)) になり、if によってキャッチされません。– ハイド 58分前
うーん、
INTMAX_MAX == UINTMAX_MAX および i = -INTMAX_MAX と仮定
uintmax_t j = i;
このコマンドの後 j = -INTMAX_MAX + (UINTMAX_MAX + 1) = 1
もし (私 < 0) {
i は 0 より小さいので、if 内でコマンドを実行します。
j = -j;
このコマンドの後 j = -1 + (UINTMAX_MAX + 1) = UINTMAX_MAX
これが正解なので、エラーの場合にトラップする必要はありません。