0

最初に、これで C を学ぶのは 4 日目なので、私のコードがきれいで簡潔でない場合はご容赦ください。また、すべての数学関数を書く練習をしたいと思ったのは、自分で書いたほうが得になると感じたからです。私のコンパイラは GNU gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 です。

度をラジアンに変換する関数を書いたときに、このバグに遭遇しました。15 度のラジアン値をソース コードに書き込んだところ、正しい答えが得られました。次に、ラジアンから度を変換する関数を作成し、それを使用して 15 度のラジアン値を取得し、sin(x) 関数にプラグインしましたが、正しくない値が返されました。

次に、さまざまなシナリオをテストして、コードを試してデバッグしました (すべての *printf* でわかるように)。次に、計算されたラジアン値の値を (10 進数で 54 桁まで)出力し、その数値をソース コードにコピーしました。答えは正しく出てきました。

だから私の質問は次のとおりです。計算された値を使用することは、同じ計算された値を使用することとは異なりますが、ソースコードに直接コピーされるシナリオは何ですか?

関数が受け取るパラメーターと同じ型でない場合、コンパイラーは自動的にリテラル値をキャストすることを理解していますが、私の場合、それがどのように異なるのかわかりません。

これがコードです。繰り返しますが、非常に遅い場合は申し訳ありませんが、これは学習目的のみです:)助けていただければ幸いです。これを引き起こす可能性のあるものに頭を悩ませることはできず、4時間の調査はどこにもつながりませんでした.

#include <stdio.h>

#define EPSILON 0.000001
#define PI 3.14159

double sin(double x);
int fact(int x);
double powr(double x, int power);
double deg_to_rad(double degrees);

int main(int argc, char *argv[]){

    double radian = deg_to_rad((double)15);

    printf("Correct answer = 0.25881883\n\n");
    printf("Sin approximation using constant .26179917: %1.8f\n\n", sin(.26179917));
    printf("Radian variable calculated by deg_to_rad(15): %1.54f\n", radian);
    printf("Sin approximation using radian variable: %1.8f\n\n", sin(radian));
    printf("Sin approximation using radian variable's value (0.261799166666666638381144593950011767446994781494140625) copied and pasted into source: %1.8f\n", sin(0.261799166666666638381144593950011767446994781494140625));

    return 0;
}

double deg_to_rad(double degrees){

    return ((degrees/180) * PI);
}

double abs_val(double x){

    if (x < 0){
        return (-x);
    }

    else{
        return x;
    }
}

int fact(int x){

    if (x > 1){
        return (x * fact(x - 1));
    }

    else return 1;
}

double powr(double x, int power){

    if (power < 1){

        if (power == 0){
            return 1.0L;
        }

        else{
            puts("This functionality is not yet built in to this function.");
            return 0.0L;
        }
    }

    else if (power > 1){
        return (x * powr(x , power - 1));
    }

    //If power is 1.
    else return x;
}

/*
* Based on the Taylor series: sin(x) = x - x^3/3! + x^5/5! - x^7/7! + x^9/9! - ... +  (-1)^(n-1)*x^(2n-1)/(2n-1)! + ....
*/
double sin(double x){

    static int n = 0;
    n++;

    double result = (powr(-1.0L,n - 1) * (powr(x, (2*n - 1)) / fact(2*n - 1)));

    //If the current n is odd.
    if (n % 2){

        if (abs_val(result) > EPSILON){
            return (result + sin(x));
        }

        else {
            //Reset n and now begin returning from recursions.
            n = 0;
            return result;
        }
    }

    else {

        if (abs_val(result) > EPSILON){
            return (-result +  sin(x));
        }

        else {
            //reset n and now begin returning recurstion
            n = 0;
            return result;
        }
    }
}
4

2 に答える 2

3

呼び出しsin(constant)は、コンパイル時に組み込みの正弦関数ではなく、sin. フラグを使用してコンパイルする-fno-builtinか、関数の名前を変更すると、コードを使用して定数の値も計算され、同じ (間違った) 結果が得られます。

間違った結果の原因は

if (abs_val(result) > EPSILON){
    return (-result +  sin(x));
}

万が一の場合n。記号はすでに に組み込まれているので、それも、奇数の場合と同様に組み込まれresultているはずです。したがって、両方のブランチにまったく同じコードが含まれている必要があるため、ブランチを削除する必要があります。return result + sin(x)n

さらに、引数が 12 より大きい場合 (符号付き 32 ビットints の場合) 、階乗はオーバーフローsinするため、目的の精度に到達するために 6 つ以上の項が必要な大きな絶対値の引数に対して関数は機能しません。

于 2012-07-09T05:53:26.713 に答える
0

ダニエルのヒントに加えて、私はEPSILONあなたの見積もりよりも高い精度レベルに設定しませんPI. まあ、それらは独立していますが、sinラジアン変換への不正確な度数に対して本当に正確に計算することになります.

コードのもう少しコンパクトなバージョンを次に示します。

static double r_sin(double xx, double term, int n, double result) {
    double r = term * xx/(2*n * (2*n+1));
    if (r > EPSILON) {
        if (n % 2) return r_sin(xx, r, n+1, result-r);
        return r_sin(xx, r, n+1, result+r);
    }
    return result;
}

double my_sin(double x){
    if (x > EPSILON) return r_sin(x*x, x, 1, x);
    return 0;
}
于 2012-07-09T06:28:30.913 に答える