Цикл for в C ++, использующий двойной разрыв на один шаг раньше, граничное значение не достигнуто

У меня есть простая программа на C ++, скомпилированная с использованием gcc 4.2.4 на 32-битной Ubuntu 8.04. Оно имеетforпетля, в которойdouble переменная увеличивается от нуля до единицы с определенным размером шага. Когда размер шага0.1, поведение то, что я ожидал. Но когда размер шага равен 0,05, цикл завершается после0.95, Может кто-нибудь сказать мне, почему это происходит? Вывод соответствует исходному коду ниже.

#include <iostream>

using namespace std;

int main()
{
    double rangeMin = 0.0;
    double rangeMax = 1.0;
    double stepSize = 0.1;

    for (double index = rangeMin; index <= rangeMax; index+= stepSize)
    {
        cout << index << endl;
    }
    cout << endl; 

    stepSize = 0.05;
    for (double index = rangeMin; index <= rangeMax; index+= stepSize)
    {
        cout << index << endl;
    }

    return 0;
}

ВЫХОД

[email protected]:~/code/scratch$ ./a.out 
0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1

0
0.05
0.1
0.15
0.2
0.25
0.3
0.35
0.4
0.45
0.5
0.55
0.6
0.65
0.7
0.75
0.8
0.85
0.9
0.95
[email protected]:~/code/scratch$
 Ahmed Said17 авг. 2009 г., 08:20
может быть проблема с плавающей точкой, я должен проверить !!!
 GManNickG17 авг. 2009 г., 08:27
Не принимайте ответы сразу! Дайте людям некоторое время, чтобы добраться до вашего вопроса и введите ответ.

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

когда вы сравниваете двойные числа, простое сравнение недостаточно, и вы должны сравнивать их «с точностью». т.е.

if ( fabs(double1-double2) < 0.0000001 ) {
  do-something
}

Проблема возникает из-запредставление двойных переменных.

ающей запятой. Ваш размер шага на самом деле не 0,1 или 0,05, это другое значение, которое очень близко. Небольшая ошибка накапливается при прохождении цикла.

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

index значение будет как1.00000001.

Решение Вопроса

ется точно представимым,0.95+0.05 > 1 потому что0.95 не совсем представимоdouble стоимость.

Посмотрите, что Википедия должна сказать оточность с плавающей точкой.

Если вы посмотрите наIEEE преобразователь с плавающей точкой вы увидите, что значение0.95 в 64-битной плавающей запятой (double) является0-01111111110-1110011001100110011001100110011001100110011001100110 введя это вкалькулятор с плавающей точкой Вы получаете значение0.95000016 и добавление0.05 к тому, что берет вас за1.0 отметка.

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

 Matthew Scharley17 авг. 2009 г., 08:51
В этом конкретном примере лучшим решением может быть вычислениеindex как целое число, и сравнитьindex * stepSize.

нецелесообразно использовать нецелые числа в циклах for, поэтому я предлагаю сделать в качестве следующего примера, чтобы сохранить точность целых чисел и получить требуемые десятичные дроби:

#include<iostream>
#include<cmath>
#include<iomanip>
using namespace std; 

int main()
{
for (double y = 1; y!=10; y += 1)
    cout << static_cast<double>(y/10) << endl; 



}

это хорошо известная проблема из-за неточного представления некоторых десятичных чисел в памяти. Я очень рекомендую прочитатьЧто каждый ученый должен знать об арифметике с плавающей точкой а такжеIEEE представления с плавающей точкой действительных чисел.

= для двойников из-за их внутреннего представления. На последнем шаге вы получите0.95000000000000029, Вместо этого вы можете использовать следующий код:

stepSize = 0.05;
// stepSize/2 looks like a good delta for most cases
for (double index = rangeMin; index < rangeMax+stepSize/2; index+= stepSize)
{
    cout << index << endl;
}

Для более подробной информации читайтеЧто каждый ученый должен знать об арифметике с плавающей точкой.

ния в арифметике с плавающей запятой.

Вам нужно прочитать ГолдбергаЧто каждый ученый должен знать об арифметике с плавающей точкой.

Смотрите этот вывод: (точность с плавающей запятой)

#include <iostream>
#include <iomanip>
using namespace std;
int main(){
    double rangeMin = 0.0;
    double rangeMax = 1.0;
    double stepSize = 0.1;
    double index;
    for (index = rangeMin;  index <= rangeMax; index+=stepSize)
        {
               cout << fixed << setprecision(16) <<  index << endl;
         }
  cout << endl;
  stepSize = 0.05;
  for (index = rangeMin; index<= rangeMax; index+= stepSize)
     {
         cout << index << endl;
             }

   cout << "\n" << setprecision(16) << index << " "  << rangeMax;
   if(index==rangeMax)
      cout << "\nEQ";
   else
     cout << "\nNot EQ";
     return 0;
}

0.0000000000000000
0.1000000000000000
0.2000000000000000
0.3000000000000000
0.4000000000000000
0.5000000000000000
0.6000000000000000
0.7000000000000000
0.7999999999999999
0.8999999999999999
0.9999999999999999

0.0000000000000000
0.0500000000000000
0.1000000000000000
0.1500000000000000
0.2000000000000000
0.2500000000000000
0.3000000000000000
0.3500000000000000
0.4000000000000000
0.4500000000000000
0.4999999999999999
0.5499999999999999
0.6000000000000000
0.6500000000000000
0.7000000000000001
0.7500000000000001
0.8000000000000002
0.8500000000000002
0.9000000000000002
0.9500000000000003

1.0000000000000002 1.0000000000000000
Not EQ

не каждое действительное число точно представляется в виде значения с плавающей запятой, поэтому вы можете ожидать небольшую «случайную» ошибку округления в вычислениях с плавающей запятой. Это похоже на то, что происходит с обычными десятичными цифрами: 1/3 не совсем точно представляется с использованием трех десятичных цифр (0,33), поэтому (1/3) * 3 станет 0,99, а не точно 1.

Можно использовать некоторую «точность» в ваших сравнениях, но я бы рекомендовал избегать чисел с плавающей запятой для циклов и вместо этого использовать целые числа.

Например, ваш цикл

stepSize = 0.05;
for (double index = rangeMin; index <= rangeMax; index+= stepSize)
{
    cout << index << endl;
}

может быть заменено чем-то вроде

stepSize = 0.05;
for (int index = 0; index < 21; ++index)
{
    double value = rangeMin + index * stepSize;
    cout << value << endl;
}

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