Почему эти два варианта кода дают разные результаты с плавающей точкой?

Учитывая этот пример фрагмента кода 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; }

Несмотря на то, что я хорошо спросил, компилятор, по-видимому, не встроил это и вычислил значение отдельно, обрезая результат перед возвратом значения в выражение, снова искажая результат. Уч.

Ответы на вопрос(1)

Ваш ответ на вопрос