0

これは私の最初の C の宿題なので、私の間違いはあなたにとって本当に明白なものかもしれません。その場合は事前にお詫び申し上げます。ただし、これは私には非常に珍しいように思えた (機能しているだけでなく、奇妙な出力が得られた) ので、おそらく周りに尋ねるべきだと思いました。交互高調波級数の和を計算しようとしています。これは、それを実行することになっている関数です (double を返します)。

    double series(void){

    double sum = 0.0;
    int i,x;

    for (i = 1; i > 0 ; i = i+1)
    {

            if (sum - (sum - 1.0/x) == 0 || sum - (sum + 1.0/x) == 0)
            {
                    printf("To infinity and beyond... almost!\n");
                    return sum;
            }
            else if (i % 2 != 0)
            {
                    sum = sum - 1.0/x;
            }
            else if (i % 2 == 0)
            {
                    sum = sum + 1.0/x;
            }
            else
            {
            printf("How did I end up here?\n");
            }

    }

    printf("I ESCAPED THE LOOP\n");
    return 1.0*i;

}

そして、プログラムを実行すると、常に最後の数行で終了します(条件が満たされた場合、最初の行まで実行することになっている間、I ESCAPED THE LOOPの行を出力するため、forループは終了します。到達する)。出力 (関数の戻り値) は、i に対して -21473822648.000000 です。負の数 (実際には、メインの出力は正ですが、そこにマイナス 1 が掛けられており、-1.0 を削除しても符号が本来あるべきでないものに変更されるだけです)、変更すべき唯一のものがある場合for ループの導入で i=1 および ++i になります。それはどのように起こりますか?main が行うことは、関数の呼び出し、関数の戻り値の負の値として double の値を設定し、その値を出力することだけです。

そして、if() ステートメントの操作の順序が間違っている可能性があることに気付きましたが、すべてを余分な括弧で囲んで修正しました。効果はありませんでした。

ああ、これは実際には問題のすべてではありません。実際には、合計の最良の次数と最悪の次数を見つけることになっています。それを行うプログラムを書くことさえできるかどうかを見たかっただけです。浮動小数点演算関連の資料などへのリンクを歓迎します。

事前に、すべてのアドバイスに感謝します。

4

1 に答える 1

1

修正された関数の使用 (コメントごと)

double series(void){

    double sum = 0.0, x;
    int i;

    for (i = 1; i > 0 ; i = i+1)
    {
        x = 1.0*i;
        if (sum - (sum - 1.0/x) == 0 || sum - (sum + 1.0/x) == 0)
        {
            printf("To infinity and beyond... almost!\n");
            return sum;
        }
        else if (i % 2 != 0)
        {
            sum = sum - 1.0/x;
        }
        else if (i % 2 == 0)
        {
            sum = sum + 1.0/x;
        }
        else
        {
            printf("How did I end up here?\n");
        }
    }
    printf("I ESCAPED THE LOOP\n");
    return 1.0*i;
}

初期化されていない変数を使用して未定義の動作を排除するには、何が起こるか見てみましょう。

の値sumは常に と の間-1.0にあり0.0、(モジュロ浮動小数点の丸め) -log 2(自然対数) に近づいています。

iですintint得られた結果から、(厳密に論理的にではなく、合理的な疑いの余地なく) s がプラットフォームに 32 ビットあると推測できますi < 2^31 = 2147483648。したがって、加算または減算される項は、1.0/iよりも大きくなり2^(-31)ます。s は通常 53 ビットの精度を持つためdouble、各加算/減算はsum十分に変化します。

sum - (sum - 1.0/x) == 0 || sum - (sum + 1.0/x) == 0

真になることはありません。そのため、ループはその条件で終了せず、iに達するまでインクリメントされINT_MAX、その後もう一度繰り返されます。

その最後のインクリメントはオーバーフローします。符号付き整数のオーバーフローは未定義の動作を引き起こしますが、この場合、ラップアラウンドの一般的な動作が発生し、i値が含まれますINT_MIN = -2147483648。次に、ループ条件i > 0が false と評価され、ループreturnは関数から ing なしで終了します。

于 2012-11-03T19:48:48.203 に答える