Ошибка плавающей точки во время цикла в С++

int main()
 {
      float x = k ; // k is some fixed positive value 
      while(x>0)
           x-- ;
      return 0 ;
 }

Может ли программа выше в бесконечном цикле?

Ответ 1

Да, это возможно. В качестве примера возьмем максимум float.

Как показывает этот код, для самого большого значения float m, m равно m - 1:

#include <iostream>
#include <limits>

int main() {
    auto m = std::numeric_limits<float>::max();
    auto l = m;
    l--;
    std::cerr << (m == l) << "\n";
}

Демо: http://ideone.com/Wr9zdN

Следовательно, с этим стартовым значением цикл будет бесконечным.

Почему это?

float имеет (как и любой другой встроенный тип) ограниченную точность. Чтобы сделать x - 1 представимым числом, отличным от x, разница между наибольшим числом меньше x должна быть меньше 2.

Теперь позвольте рассчитать разницу между m, максимальным поплавком и x, самым большим поплавком, который строго меньше m:

#include <iostream>
#include <cmath>
#include <limits>

int main() {
    auto m = std::numeric_limits<float>::max();
    std::cout << "float max: " << m << "\n";
    auto x = std::nextafter(m, 0.0f);
    std::cout << "biggest value less than max: " << x << "\n";
    auto d = m - x;
    std::cout << "the difference: " << d << "\n";
}

Демо: http://ideone.com/VyNgtE

Оказывается, между этими двумя числами существует огромный пробел 2.02824e+31. Дальше больше 1. 1 просто слишком мало, чтобы иметь значение.

Ответ 2

Да, может. Если k - FLT_MAX, например. Недостаточно точности для обработки такого малого расстояния между такими большими числами.

#include <float.h>
#include <stdio.h>

int main()
{
    float a = FLT_MAX;
    float b = a - 1;
    printf("%.10f\n", a - b);

    return 0;
}

Вывод:

0.0000000000

Ответ 3

Я думаю, что это возможно. Если k достаточно большое округление, вы поглотите свой декремент.