Почему эти два варианта кода дают разные результаты с плавающей точкой?
Учитывая этот пример фрагмента кода C ++:
void floatSurprise()
{
// these come from some sort of calculation
int a = 18680, b = 3323524, c = 121;
float m = float(a) / c;
// variant 1: calculate result from single expression
float r1 = b - (2.0f * m * a) + (m * m * c);
cout << "r1 = " << r1 << endl;
// variant 2: break up the expression into intermediate parts,
/// then calculate
float
r2_p1 = 2.0f * m * a,
r2_p2 = m * m * c,
r2 = b - r2_p1 + r2_p2;
cout << "r2 = " << r2 << endl;
}
Выход:
dev1 = 439703
dev2 = 439702
При просмотре в отладчике значения на самом деле равны 439702.50 и 439702.25 соответственно, что само по себе интересно - не уверен, почему распечатки iostream по умолчанию плавают без дробной части.РЕДАКТИРОВАТЬ: Причиной этого было то, что значение точности по умолчанию для cout было слишком низким, требовалось cout << setprecision (7) хотя бы для того, чтобы увидеть десятичную точку для чисел этой величины.
Но меня еще больше интересует, почему я получаю разные результаты. Я полагаю, что это связано с округлением и некоторым тонким взаимодействием целых с требуемым типом вывода с плавающей запятой, но я не могу это понять. Какое значение является правильным?
Я был поражен, что так легко выстрелить себе в ногу с таким простым фрагментом кода. Любое понимание будет с благодарностью! Компилятор был VC ++ 2010.
EDIT2: Я провел еще несколько исследований, используя электронную таблицу для генерации «правильных» значений для промежуточных переменных, и обнаружил (посредством трассировки), что они действительно обрезаются, что способствует потере точности в конечном результате. Я также обнаружил проблему с одним выражением, потому что я фактически использовал удобную функцию для вычисления квадратов вместоm * m
там:
template<typename T> inline T sqr(const T &arg) { return arg*arg; }
Несмотря на то, что я хорошо спросил, компилятор, по-видимому, не встроил это и вычислил значение отдельно, обрезая результат перед возвратом значения в выражение, снова искажая результат. Уч.