Ряд Тейлора для расчета косинуса (получение выходного значения -0,000 для косинуса (90)) - PullRequest
0 голосов
/ 07 октября 2019

Я написал следующую функцию для ряда Тейлора для вычисления косинуса.

double cosine(int x) {
    x %= 360; // make it less than 360
    double rad = x * (PI / 180);
    double cos = 0;

    int n;

    for(n = 0; n < TERMS; n++) { 
        cos += pow(-1, n) * pow(rad, 2 * n) / fact(2 * n);
    }
    return cos;
}

Моя проблема заключается в том, что при вводе 90 я получаю ответ -0,000000. (почему я получаю -0,000 вместо 0,000?)

Кто-нибудь может объяснить, почему и как я могу решить эту проблему?
Я думаю, что это связано с точностью double. Вот главная ():

int main(void){
int y;
//scanf("%d",&y);
y=90;
printf("sine(%d)= %lf\n",y, sine(y));
printf("cosine(%d)= %lf\n",y, cosine(y));

return 0;
}

1 Ответ

2 голосов
/ 07 октября 2019

Вполне ожидаемо, что вы не сможете получить точные нулевые выходы для косинуса всего с плавающей запятой, независимо от того, насколько хорош ваш подход к вычислениям. Это фундаментально для того, как работает плавающая точка.

Математические нули косинуса - это нечетные кратные числа пи / 2. Поскольку pi иррационально, оно не совсем представимо в виде двойного числа (или любой формы с плавающей запятой), и разница между ближайшими соседними значениями, которые представимы, будет, по крайней мере, в пи / 2 раза DBL_EPSILON, примерно 3e-16 (или соответствующие значения для других типов с плавающей запятой). Для некоторых нечетных кратных числа pi / 2 вам может «повезти» и вы обнаружите, что он действительно близок к одному из двух соседей, но в среднем вы обнаружите, что он находится на расстоянии 1e-16. Таким образом, ваш ввод уже неверен на 1e-16 или около того .

Теперь косинус имеет наклон +1 или -1 в своих нулях, поэтому ошибка на выходе будет примерно пропорциональнаошибка на входе. Но чтобы получить точный ноль, вам понадобится ошибка меньше, чем наименьший представимый ненулевой дубль, который составляет около 2e-308. Это почти на 300 порядков меньше, чем погрешность на входе.

В то время как вы теоретически можете «повезти» и получите несколько кратных, если pi / 2 действительно очень близко к ближайшему представимому двойному значению, вероятностьэто, просто моделирование его как случайного, астрономически мало. Я полагаю, что есть даже доказательства того, что нет двойного x, для которого правильно округленное значение cos(x) является точным нулем. Для одинарной точности (float) это можно легко определить методом грубой силы;для double это, вероятно, также выполнимо, но большое вычисление.

Что касается того, почему printf печатает -0.000000, просто для %f по умолчанию используется 6 знаков после десятичной точки, чтонедостаточно близко, чтобы увидеть первую значащую цифру. Использование %e или %g, опционально с модификатором большой точности, покажет вам приблизительный результат, который вы получили, который действительно сохраняет некоторую значимость, и даст вам представление о том, хорош ли ваш результат.

...