Почему этот код возвращает два разных значения, делающих то же самое?
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main()
{
double a;
double b;
double q0 = 0.5 * M_PI + 0.5 * -2.1500000405000002;
double q1 = 0.5 * M_PI + 0.5 * 0.0000000000000000;
double w0 = 0.5 * M_PI + 0.5 * -43000.0008100000050000;
double w1 = 0.5 * M_PI + 0.5 * -0.0000000000000000;
double m = 1;
double g = 43000000.81;
double l1 = 0.1;
double l2 = 0.1;
double h = 0.0001;
a = ((-g / l1) * sin(q0) + (sin(q1 - q0) * (cos(q1 - q0) * (w0 * w0 + (g / l1) * cos(q0)) + l2 * (w1 * w1 / l1))) / (m + pow(sin(q1 - q0), 2)));
a = h * a;
b = h * ((-g / l1) * sin(q0) + (sin(q1 - q0) * (cos(q1 - q0) * (w0 * w0 + (g / l1) * cos(q0)) + l2 * (w1 * w1 / l1))) / (m + pow(sin(q1 - q0), 2)));
printf("%.20lf ", a);
printf("%.20lf", b);
return 0;
}
Я делаю те же вычисления с a и b, только с той разницей, что получаю значение a за два шага, а b за один.
Мой код возвращает: -629.47620126173774000000 -629.47620126173763000000
В чем причина разницы между двумя последними десятичными знаками?
2 ответа
Стандарт C (99 и 11) гласит:
Значения операций с плавающими операндами и значениями, подверженными обычным арифметическим преобразованиям и плавающим константам, оцениваются в формате, диапазон и точность которого могут быть больше, чем требуется типом.
Так в выражении, таком как h*(X+Y)
как у вас в задании b
реализация позволяет использовать большую точность для промежуточного результата X+Y
чем может быть сохранено в double
, хотя тип подвыражения все еще считается double
, Но в a=X+Y; a=h*a;
первое назначение заставляет значение быть тем, которое фактически может быть сохранено в double
, вызывая немного другой результат.
Другая возможность состоит в том, что компилятор сделал "сжатие с плавающей запятой". Чтобы процитировать стандарт C снова,
Плавающее выражение может быть сжато, то есть вычислено так, как если бы оно было атомарной операцией, тем самым исключая ошибки округления, подразумеваемые исходным кодом и методом оценки выражения.
Скорее всего, это произойдет, если у процессора есть одна инструкция, которая может выполнять сложение с плавающей запятой, а затем умножение за один шаг, и компилятор решил ее использовать.
Предполагая, что один или оба из них являются причиной, ваша ценность b
вероятно, более точное представление вычислений, которые вы указали (учитывая, что все входные данные были ограничены значениями, которые могут быть представлены в double
).
Страница cppreference о макросе FLT_EVAL_METHOD
обсуждает оба эти вопроса чуть более подробно. Может быть интересно узнать вашу ценность FLT_EVAL_METHOD
и играть с #pragma STDC FP_CONTRACT OFF
,
Ответ заключается в том, что в вычислениях чисел с плавающей точкой уравнение a = bc de не обязательно должно быть равно x = b c y = de и a.= X y.
Это потому, что арифметика с плавающей запятой имеет ограниченную точность.