Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > математическая ошибка при прохождении цикла


Автор: nubliK 20.1.2009, 02:11
Не знаю как у других, у меня почему то возникает накопление ошибки при прохождении цикла, вот код:
Код

int someFunc(float A)
  {
      int n = 0;
      while(A > 6)
      {
         A = A - 0.1;
         n++;
      }
   return n;
   }


Компилится все правильно и работает тоже (в данном примере функция немного отличается от оригинала), дык вот если числу "A" задать значение больше 13 то цикл начинает врать на еденицу - как побороть?

Автор: NightmareZ 20.1.2009, 02:19
Цитата(nubliK @  20.1.2009,  02:11 Найти цитируемый пост)
как побороть?


Приведённый тобой код можно заменить на такой:

Код

int someFunc(float A)
{
  return A * 60;
}


И никакой цикл тебе врать ничего не будет.

Автор: mes 20.1.2009, 02:23
Цитата(nubliK @  20.1.2009,  01:11 Найти цитируемый пост)
то цикл начинает врать на еденицу - как побороть? 

скорей всего из за ошибки округления цифр с плавающей запятой. 
исправьте цикл на абсолютный расчет или избавьтесь от плавающих значений.


Цитата(NightmareZ @  20.1.2009,  01:19 Найти цитируемый пост)
  return A * 60;

хм... непонятно как из примера ТС, Вы получили такой код

Автор: nubliK 20.1.2009, 04:10
Цитата(NightmareZ @ 20.1.2009,  02:19)
Цитата(nubliK @  20.1.2009,  02:11 Найти цитируемый пост)
как побороть?


Приведённый тобой код можно заменить на такой:

Код

int someFunc(float A)
{
  return A * 60;
}


И никакой цикл тебе врать ничего не будет.

Бред сивой кобылы мягко говоря. Смотри код внимательно.
Цитата(mes)

скорей всего из за ошибки округления цифр с плавающей запятой. 
исправьте цикл на абсолютный расчет или избавьтесь от плавающих значений.


Как это сделать? и переменная обязательно должна быть с плавающей точкой. Вычитаемое может быть и не 0.1 но и 0.05 и хоть десять.

Добавлено через 12 минут и 55 секунд
прошу прощение за грубость у NightmareZ =)

Автор: nubliK 20.1.2009, 06:40
нашел небольшую статейку по теме тут http://xpoint.ru/know-how/Articles/FloatingPointNumbers?comments
но вопрос остался в силе =)
И еще если это никого не затруднит не могли бы вы привести пример варианта как можно задать точность округления числа с плавающей точкой (то есть точность до определенного знака).

Автор: ksili 20.1.2009, 06:49
Можно использовать числа с плавающей точкой двойной точности - тип double. Или в крайнем случае - расширенной точности (long double).
Указать точность округления до определенного знака при вычислениях не получится.

Автор: Kurganec 20.1.2009, 07:23
попробуй while(A > 6.)  //с точкой
или while(A>6.*0.99999) // близкий к  100%

последнее конечно бред, но у меня тоже проблемы в своё время были при сравнении вещественных переменных, с целыми.
отпишись тока  smile  cсамому интересно.

Автор: nubliK 20.1.2009, 07:44
Всем огромное спасибо, вот мое решение получилось так:

Код

int someFunc(double A) //изменил тип - потому что мне его достаточно
  {
      int n = 0;
      while(A > 6)
      {
         A =(float)A - 0.1; //преобразовал тип
         n++;
      }
   return n;
   }


Получается что компу нехватает точности чтобы хранить число с плавающей точкой - соответственно решение получилось через приведение типов от double к float =)

Если кто придумает правельнее то ХАЧУ!
кстати тут вот подобная дисскуссия вилась и слизал идею я отсюда http://forum.codenet.ru/archive/index.php/t-24605.html

Автор: vinter 20.1.2009, 08:52
Код

float epsilon = 0.000001;
while((A - epsilon) > 6)
{
   A -= 0.1;
  n++;
}

Автор: maxim1000 20.1.2009, 09:31
а если так: smile
Код

int someFunc(float A)
  {
      int n = 0;
      while(A > 6)
      {
         A = A - 0.25;
         n++;
      }
   return n;
   }

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

Добавлено через 1 минуту и 3 секунды
Цитата(vinter @  20.1.2009,  08:52 Найти цитируемый пост)
float epsilon = 0.000001;
while((A - epsilon) > 6)
  n++;

что-то здесь забыто: цикл либо не выполнится (если условие изначально ложное), либо будет бесконечным

Автор: vinter 20.1.2009, 09:43
maxim1000, спасибо, исправил

Автор: nubliK 20.1.2009, 13:14
А вот и правильный ответ:

Код

int someFunc(float A)
  {
      int B = static_cast<int>(A*10)  //множитель "10" можно менять в зависимости от значения вычитаемого, static_cast<int> - уходим от типа с плавающей точкой.
      int n = 0;
      while(A > 60) //не забываем про множитель.
      {
         B = B - 1; //не забываем про множитель и вычитаемое.
         n++;
      }
   return n;
   }


Спасибо товарищу mes т.к. он прав что в таких случаях лучший вариант уйти от плавающей точки.
smile  

Автор: baldina 20.1.2009, 18:11
mes:
Цитата

исправьте цикл на абсолютный расчет

 smile 

данный случай настолько прост, что его даже обсуждать не стоит: здесь вообще цикл не нужен.

а вот если он нужен, то, например, вместо

Код

double a = ..., b = ...;
int n = ...;

double step = (b-a)/n;
for (double x = a; x <= b; x += step)
{
   cout << f(x);
}


делаем

Код

double a = ..., b = ...;
int n = ...;

double step = (b-a)/n;
for (int i=0; i < n; ++i)
{
   double x = a+i*step;
   cout << f(x);
}


сравнение чисел с плавающей можно производить так:
Код

namespace impl {

  template <typename T, bool>
  struct is_equal {
    bool operator ()(T x, T y, T Epsilon);
  };

  template <typename T>
  struct is_equal<T,true> {
    bool operator ()(T x, T y, T) { return x==y; }
  };

  template <typename T>
  struct is_equal<T,false> {
    bool operator ()(T x, T y, T epsilon) { 
      if (x==y)
        return true;
      using namespace std;
      return abs (x-y) / max (abs(x), abs(y)) < epsilon;
    }
  };

} // namespace impl

template <typename T>
bool is_equal (T x, T y, T Epsilon = pow (10.0,-std::numeric_limits <T>::digits10))
{
  impl::is_equal <T, std::numeric_limits <T>::is_exact> is_eq;
  return is_eq (x, y, Epsilon);
}

Автор: GoldFinch 20.1.2009, 18:56
Цитата(maxim1000 @  20.1.2009,  09:31 Найти цитируемый пост)
те, которые в двоичном виде представляются в виде конечных (и не очень длинных) дробей, по идее, должны представляться точно, хотя это уже похоже на деталь реализации, к которой не стоит привязываться...

неужели троичные машины всетаки появятся?

Автор: baldina 20.1.2009, 19:32
они уже давно появились. некоторых еще на свете не было  smile 
http://chernykh.net/content/view/236/252/

Автор: GoldFinch 20.1.2009, 21:12
baldina, а ссылку на компилятор С/С++ для таких машин подкинете?

Автор: baldina 20.1.2009, 21:53
не кину. да и зачем компилятор без машины?

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)