Как обойти проблемы округления в арифметике с плавающей запятой в C ++? - PullRequest
2 голосов
/ 02 декабря 2010

У меня проблемы с арифметикой с плавающей запятой. Я пытаюсь рассчитать оценку на основе взвешенной формулы, где каждая входная переменная весит примерно в 20 раз больше следующей значимой. Входные данные, однако, являются действительными числами, поэтому я использовал двойной для сохранения результата. Приведенный ниже код имеет проблему потери разницы между E1 и E2.

Этот код чувствителен к производительности, поэтому мне нужно найти эффективный ответ на эту проблему. Я думал о том, чтобы умножить свои входные данные на сотню, а затем использовать int (поскольку я думаю, это было бы достаточно точно), но я сомневаюсь, что это лучшее решение, поэтому вопрос.

#include <iostream>

int main()
{
    double score1, score2;
    float a  = 2.75 ;
    float b  = 5.25 ;
    float c  = 5.25 ;
    float d  = 2.75 ;
    float E1 = 3    ;
    float E2 = 6    ;

    score1 = 20 * b - 1 * a + 0.05 * d  /* - 0.0025 * c*/ + 0.0001 * E1 ;
    score2 = 20 * b - 1 * a + 0.05 * d  /* - 0.0025 * c*/ + 0.0001 * E2 ;

    std::cout << score1 << std::endl;
    std::cout << score2 << std::endl;

    std::cin.get();
    return 0;
}

//ouputs:
//102.388
//102.388

Ответы [ 4 ]

4 голосов
/ 02 декабря 2010
  1. вы не выводите все значение, используйте cout << setprecision(number_of_digits) << score1 << endl;
  2. сколько правильных цифр вам нужно для вычисления счета?
3 голосов
/ 02 декабря 2010

Я думал о том, чтобы умножить свои входные данные на сотню, а затем использовать int (поскольку я думаю, это было бы достаточно точно), но я сомневаюсь, что это лучшее решение

Учитывая значения, которые вы показали, я бы сказал, что это так.

1 голос
/ 02 декабря 2010

Посмотрим, что происходит в этом коде:

score1 = 20 * b - 1 * a + 0.05 * d  /* - 0.0025 * c*/ + 0.0001 * E1 ;

// Multiplication division happens first:

float  tmp1 = static_cast<float>(20) * b;      // 20 cast to float.
float  tmp2 = static_cast<float>(1)  * a;      // 1  cast to float.
double tmp3 = 0.05   * static_cast<double>(d); // d converted to double as 0.05 is double
double tmp4 = 0.0001 * static_cast<double>(E1);// E1 cast to double as 0.0001 is double

// Addition and subtraction now happen
float  tmp5  = tmp1 - tmp2;
double tmp6  = static_cast<double>(tmp5) + tmp3; // tmp5 cast to double as tmp3 is a double.
double tmp7  = tmp6 + tmp4;
score1       = tmp7;

Если мы сделаем это в наших головах:

tmp1 = 105.0
tmp2 =   2.75
tmp3 =   0.1375
tmp4 =   0.0003
tmp5 = 107.75
tmp6 = 107.8875
tmp7 = 107.8878

Точность должна сохраняться для этих значений:
Но при распечатке точность по умолчанию для двойных чисел составляет 3 десятичных знака.

std::cout << 107.8878;
> 107.888

Итак, установите точность:

std::cout << std::setprecision(15) << 107.8878 << "\n";
> 107.8878
1 голос
/ 02 декабря 2010

http://ideone.com/qqTB3 показывает, что разница не потеряна, а на самом деле настолько велика, насколько вы ожидаете (с точностью до плавающей запятой, которая равна 15 десятичных цифр для двойного).

...