言語がデフォルトで整数オーバーフロー時にエラーを発生させないのはなぜですか?

StackOverflow https://stackoverflow.com/questions/103654

質問

いくつかの最新のプログラミング言語 (C++、Java、C# など) では、この言語で次のことが可能です。 整数オーバーフロー いかなる種類のエラー状態も発生させることなく、実行時に発生します。

たとえば、オーバーフロー/アンダーフローの可能性が考慮されていない、この (人為的な) C# メソッドを考えてみましょう。(簡潔にするため、このメソッドは、指定されたリストが null 参照である場合も処理しません。)

//Returns the sum of the values in the specified list.
private static int sumList(List<int> list)
{
    int sum = 0;
    foreach (int listItem in list)
    {
        sum += listItem;
    }
    return sum;
}

このメソッドが次のように呼び出された場合:

List<int> list = new List<int>();
list.Add(2000000000);
list.Add(2000000000);
int sum = sumList(list);

でオーバーフローが発生します。 sumList() メソッド( int C# の type は 32 ビット符号付き整数であり、リスト内の値の合計が最大 32 ビット符号付き整数の値を超えています)。sum 変数の値は -294967296 になります (値 4000000000 ではありません)。これは、sumList メソッドの (仮説の) 開発者が意図したものではない可能性があります。

明らかに、整数オーバーフローの可能性を回避するために開発者が使用できるさまざまな手法があります。たとえば、Java のような型を使用するなどです。 BigInteger, 、 または checked キーワードと /checked C# のコンパイラ スイッチ。

しかし、私が興味がある疑問は、これらの言語が、たとえば、実行時に操作が実行されると例外が発生するのではなく、そもそもデフォルトで整数オーバーフローの発生を許可するように設計されているのかということです。オーバーフロー。このような動作は、開発者がオーバーフローを引き起こす可能性のある算術演算を実行するコードを作成するときにオーバーフローの可能性を考慮しない場合のバグを回避するのに役立つと思われます。(これらの言語には、開発者が明示的にその動作を意図している場合、例外を発生させることなく整数オーバーフローの発生を許可するブロックを指定できる「unchecked」キーワードのようなものが含まれている可能性があります。実はC# これはあります.)

答えは単純にパフォーマンスの問題でしょうか。言語設計者は、該当するすべての算術演算でオーバーフローが発生したかどうかをチェックするためにランタイムが追加の作業を行う必要がある「遅い」整数算術演算を、それぞれの言語でデフォルトで実行することを望まなかったのです。このパフォーマンスに関する考慮事項は、不注意によるオーバーフローが発生した場合の「サイレント」障害を回避することの価値を上回っていますか?

この言語設計の決定には、パフォーマンスの考慮以外の理由もありますか?

役に立ちましたか?

解決

C# では、パフォーマンスが問題でした。具体的には、すぐに使えるベンチマークです。

C# が新しいとき、Microsoft は多くの C++ 開発者が C# に乗り換えることを期待していました。彼らは、多くの C++ 関係者が C++ が高速である、特に自動メモリ管理などに時間を「無駄にしている」言語よりも高速であると考えていることを知っていました。

潜在的な採用者も雑誌の査読者も、新しい C# のコピーを入手してインストールし、現実世界では誰も書かないような簡単なアプリを構築し、タイトなループで実行して、どれくらいの時間がかかったかを測定する可能性があります。そして、その結果に基づいて会社の決定を下したり、記事を公開したりするのです。

彼らのテストで C# がネイティブ コンパイルされた C++ よりも遅いことが示されたという事実は、人々をすぐに C# から遠ざけてしまうようなものです。C# アプリがオーバーフロー/アンダーフローを自動的に捕捉するという事実は、C# アプリでは見落とされる可能性のあるものです。したがって、デフォルトではオフになっています。

99% の場合、/checked をオンにしたいのは明らかだと思います。それは残念な妥協です。

他のヒント

パフォーマンスが十分な理由だと思います。整数をインクリメントする一般的なプログラムのすべての命令を考慮し、1 を加算する単純な演算の代わりに、1 を加算することで型がオーバーフローするかどうかを毎回チェックする必要がある場合、追加サイクルのコストはかなり深刻になります。

整数オーバーフローは常に望ましくない動作であるという前提に基づいて作業します。

場合によっては、整数オーバーフローが望ましい動作となります。私が見た 1 つの例は、絶対方位値を固定小数点数として表現したものです。unsigned int の場合、0 は 0 または 360 度であり、最大 32 ビットの符号なし整数 (0xffffffff) が 360 度のすぐ下の最大値です。

int main()
{
    uint32_t shipsHeadingInDegrees= 0;

    // Rotate by a bunch of degrees
    shipsHeadingInDegrees += 0x80000000; // 180 degrees
    shipsHeadingInDegrees += 0x80000000; // another 180 degrees, overflows 
    shipsHeadingInDegrees += 0x80000000; // another 180 degrees

    // Ships heading now will be 180 degrees
    cout << "Ships Heading Is" << (double(shipsHeadingInDegrees) / double(0xffffffff)) * 360.0 << std::endl;

}

おそらく、この例と同様に、オーバーフローが許容される状況は他にもあるでしょう。

C/C++ ではトラップ動作を強制することはありません。明らかな 0 による除算であっても、C++ では未定義の動作であり、指定された種類のトラップではありません。

C 言語には、シグナルをカウントしない限り、トラップの概念がありません。

C++ には、要求しない限り、C にはないオーバーヘッドを導入しないという設計原則があります。したがって、Stroustrup は、整数が明示的なチェックを必要とする方法で動作することを義務付けたくなかったでしょう。

一部の初期のコンパイラと、制限されたハードウェア用の軽量実装は例外をまったくサポートしておらず、多くの場合、例外はコンパイラ オプションで無効にできます。組み込み言語の例外を義務付けると問題が発生します。

たとえ C++ が整数チェックを行っていたとしても、初期のプログラマーの 99% はパフォーマンス向上のためにオフにしていたでしょう...

オーバーフローのチェックに時間がかかるためです。通常、単一のアセンブリ命令に変換される各基本的な数学演算には、オーバーフローのチェックを含める必要があり、その結果、複数のアセンブリ命令が生成され、プログラムが数倍遅くなる可能性があります。

おそらく99%のパフォーマンスです。x86 では、すべての操作でオーバーフロー フラグをチェックする必要があり、パフォーマンスに大きな影響を及ぼします。

残りの 1% は、人々が派手なビット操作を行ったり、符号付き演算と符号なし演算を混合する際に「不正確」であったり、オーバーフロー セマンティクスを必要としたりするケースをカバーします。

下位互換性は非常に重要です。C では、データ型のサイズに十分な注意を払っており、オーバーフロー/アンダーフローが発生した場合でもそれが目的であると想定されていました。次に、C++、C#、Java では、「組み込み」データ型の動作方法はほとんど変わりません。

実行時にデフォルトでエラーが発生しない理由についての私の理解は、結局のところ、ACID のような動作を持つプログラミング言語を作成したいという願望の伝統に帰着します。具体的には、実行するようにコーディングした (またはコーディングしなかった) ことはすべて実行される (または実行されない) という教義です。何らかのエラー ハンドラーをコーディングしなかった場合、マシンは、エラー ハンドラーがないため、マシンに指示されているばかげた、クラッシュしやすいことを本当に実行したいのだと「想定」します。

(ACID 参照: http://en.wikipedia.org/wiki/ACID)

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top