unsigned int
as引数を取り、int
UINT_MAX +1を法とする合同を引数に返す関数を定義したいと思います。
最初の試みは次のようになります。
int unsigned_to_signed(unsigned n)
{
return static_cast<int>(n);
}
しかし、言語弁護士なら誰でも知っているように、INT_MAXより大きい値の符号なしから符号付きへのキャストは、実装によって定義されます。
(a)仕様で義務付けられている動作のみに依存するようにこれを実装したいと思います。(b)最新のマシンおよび最適化コンパイラでno-opにコンパイルされます。
奇妙なマシンに関しては... unsignedintに対してUINT_MAX + 1を法とするsignedint合同がない場合、例外をスローしたいとしましょう。複数ある場合(これが可能かどうかはわかりません)、最大のものが必要だとしましょう。
OK、2回目の試行:
int unsigned_to_signed(unsigned n)
{
int int_n = static_cast<int>(n);
if (n == static_cast<unsigned>(int_n))
return int_n;
// else do something long and complicated
}
私の謙虚な意見ではありそうもないので、私が典型的な2の補数システムを使用していないときは、効率についてはあまり気にしません。そして、私のコードが2050年の遍在する符号と大きさのシステムのボトルネックになった場合、誰かがそれを理解して最適化できるに違いありません。
さて、この2回目の試みは、私が望むものにかなり近いものです。キャスト先int
は一部の入力に対して実装定義ですが、キャストバック先unsigned
はUINT_MAX +1を法とする値を保持するために標準によって保証されています。したがって、条件付きは私が望むものを正確にチェックし、私が遭遇する可能性のあるどのシステムでもコンパイルされません。
ただし...int
実装定義の動作を呼び出すかどうかを最初に確認せずに、まだキャストしています。2050年のいくつかの架空のシステムでは、誰が何を知っているかを実行できます。だから私はそれを避けたいとしましょう。
質問:私の「3回目の試み」はどのように見えるべきですか?
要約すると、私はしたい:
- unsignedintからsignedintにキャストします
- 値modUINT_MAX +1を保持します
- 標準で義務付けられている動作のみを呼び出す
- 最適化コンパイラを使用して、一般的な2の補数マシンでno-opにコンパイルします。
[更新]
これが些細な質問ではない理由を示す例を挙げましょう。
次のプロパティを持つ架空のC ++実装について考えてみます。
sizeof(int)
4に等しいsizeof(unsigned)
4に等しいINT_MAX
32767に等しいINT_MIN
-2 32 +32768に等しいUINT_MAX
2に等しい32 - 1- 演算には、
int
モジュロ2 32(範囲内にINT_MIN
スルーINT_MAX
) std::numeric_limits<int>::is_modulo
本当です- unsigned
n
をintにキャストすると、0 <= n <= 32767の値が保持され、それ以外の場合はゼロになります。
この架空の実装ではint
、各unsigned
値に一致する値が1つだけあります(mod UINT_MAX + 1)。したがって、私の質問は明確に定義されます。
この架空のC ++実装は、C ++ 98、C ++ 03、およびC ++ 11仕様に完全に準拠していると私は主張します。私はそれらすべての単語をすべて覚えているわけではないことを認めます...しかし、私は関連するセクションを注意深く読んだと思います。したがって、私にあなたの答えを受け入れてもらいたい場合は、(a)この架空の実装を除外する仕様を引用するか、(b)それを正しく処理する必要があります。
実際、正解は、標準で許可されているすべての仮想実装を処理する必要があります。それが、定義上、「標準で義務付けられた動作のみを呼び出す」という意味です。
ちなみに、std::numeric_limits<int>::is_modulo
ここでは複数の理由でまったく役に立たないことに注意してください。一つには、true
unsigned-to-signedキャストが大きなunsigned値に対して機能しない場合でも可能です。もう1つは、true
算術演算が整数範囲全体を法とするだけの場合、1の補数または符号の大きさのシステムでも可能です。等々。あなたの答えがに依存しているならis_modulo
、それは間違っています。
【アップデート2】
HVDの答えは整数のための私の仮定のC ++実装がされています。私に何かを教えていない符号付き整数の表現について非常に特定されている現代のC.ザ・C99とC11の規格によって許可します; 実際、それらは2の補数、1の補数、および符号の大きさのみを許可します(セクション6.2.6.2段落(2);)。
しかし、C ++はCではありません。結局のところ、この事実は私の質問の中心にあります。
元のC ++ 98標準は、はるかに古いC89に基づいていました(セクション3.1.2.5)。
符号付き整数型ごとに、同じ量のストレージ(符号情報を含む)を使用し、同じ配置要件を持つ、対応する(ただし異なる)符号なし整数型(キーワードunsignedで指定)があります。符号付き整数型の非負の値の範囲は、対応する符号なし整数型のサブ範囲であり、各型での同じ値の表現は同じです。
C89は、符号ビットが1つしかないこと、または2の補数/ 1の補数/符号の大きさだけを許可することについては何も述べていません。
C ++ 98標準は、この言語をほぼ逐語的に採用しました(セクション3.9.1段落(3)):
符号付き整数型のそれぞれについて、対応する(ただし異なる)符号なし整数型が存在します: "
unsigned char
"、 "unsigned short int
"、 "unsigned int
"、および "unsigned long int
"、それぞれが同じ量のストレージを占有し、同じ配置要件を持ちます(3.9 )対応する符号付き整数型として; つまり、各符号付き整数型は、対応する符号なし整数型と同じオブジェクト表現を持ちます。符号付き整数型の非負の値の範囲は、対応する符号なし整数型のサブ範囲であり、対応する各符号付き/符号なし型の値表現は同じでなければなりません。
C ++ 03標準は、C ++ 11と同様に、本質的に同じ言語を使用します。
私の知る限り、標準のC ++仕様では、符号付き整数表現を任意のC仕様に制約していません。そして、シングルサインビットやそのようなものを義務付けるものは何もありません。負でない符号付き整数は、対応する符号なしのサブ範囲でなければならないということだけです。
したがって、ここでも、INT_MAX = 32767、INT_MIN = -2 32 + 32768が許可されていると主張します。あなたの答えがそうではないと仮定した場合、私が間違っていることを証明するC ++標準を引用しない限り、それは正しくありません。