26

別の質問では、のトピックがstd::numeric_limits<int>::is_modulo出てきました。しかし、考えれば考えるほど、仕様またはGCC、あるいはその両方に問題があるように思われます。

いくつかのコードから始めましょう:

#include <limits>
#include <iostream>

bool test(int x)
{
    return x+1 > x;
}

int main(int argc, char *argv[])
{
    int big = std::numeric_limits<int>::max();

    std::cout << std::numeric_limits<int>::is_modulo << " ";
    std::cout << big+1 << " ";
    std::cout << test(big) << "\n";
}

これをg++ -O3 -std=c++11(x86_64 GCC 4.7.2)でコンパイルすると、次の出力が生成されます。

1 -2147483648 1

つまり、is_modulotrueであり、1つのプラスINT_MAXは負であり、1つのプラスINT_MAXは。より大きいですINT_MAX

あなたがこの質問に答える現実的なチャンスを持っているような人なら、あなたはすでにここで何が起こったのかを知っています。C ++仕様では、整数のオーバーフローは未定義動作であるとされています。コンパイラは、あなたがそうしないと想定することができます。したがって、への引数を;にするx+1ことはできませんINT_MAX。したがって、コンパイラーは関数をコンパイルして無条件testに返すことができます(そしてコンパイルします)。trueここまでは順調ですね。

ただし、C ++ 11仕様には次のようにも記載されています(18.3.2.4段落60-61)。

static constexpr is_modulo;

タイプがモジュロの場合はTrue。222、、、またはそのタイプの値を含む操作で+-結果*が範囲外になる場合、タイプはモジュロです。[min(),max()]返される値は、の整数倍だけ真の値と異なりますmax() - min() + 1

ほとんどのマシンでは、これはfalse浮動型、true符号なし整数、および符号付き整数true用です。

セクション5の段落(4)には、「式の評価中に、結果が数学的に定義されていないか、そのタイプの表現可能な値の範囲内にない場合、動作は未定義である」と記載されていることに注意してください。is_modulo == true例外を作成することについての言及はありません。

したがって、整数のオーバーフローを同時に定義および未定義にすることはできないため、標準は論理的に矛盾しているように見えます。または、少なくとも、GCCは不適合です。これは、符号付き演算が確実にラップアラウンドしない場合でも、GCCが不適合であるためis_moduloですtrue

標準のバギーですか?GCCは不適合ですか?私は何かが足りないのですか?

4

1 に答える 1

18

is_moduloが通常の算術変換によって変更されないtrue符号付き型(例)の場合int、ゼロ除算以外の算術演算の場合、モジュロが範囲内の単一の値にマップされる(数学的な)整数に単一の正しい結果があります。したがって、実装は、実際の結果が型の範囲を法とする真の結果であるかのように動作するように制約されます。したがって、実装がオーバーフロー演算を未定義として保持したい場合は、に設定is_moduloする必要がありfalseます。

これはgccメーリングリストでひどく議論され、PR 22200の下最終的にはの値は署名されたタイプのものでis_moduloなければならないという結論に達しました。今年の4月にlibstdc++に変更が加えられました。false

C ++ 03では、言語が大幅に異なることに注意してください。

18.2.1.2numeric_limitsメンバー[lib.numeric.limits.members]

56-[...] 2つの正の数を加算し、それよりも小さい3番目の数にラップアラウンドする結果が得られる場合、型はモジュロです。

未定義の動作で何でも可能であることを考えると、libstdc ++の以前の動作(is_moduloとして持つtrue)が正しく、g++の動作と一致していることは議論の余地があります。リンクされたPRに関する以前の議論は、これを念頭に置いて読む必要があります。

于 2012-11-07T17:32:33.793 に答える