2016-11-23 1 views
-2

Я искал часы, похожие на ту, что я сейчас испытываю, но я ничего не могу найти, ни здесь и на других сайтах.C++ Do-While бесконечный цикл, даже если условие выполнено (не могу понять, почему)

#include <iostream> 
using namespace std; 

int main() 
{ 
    double Numero=0, ParteDecimale=0, Controllo=0, Controllo2=0; 
    int ParteIntera=0; 
    string Var1="1", Var0="0", NumeroFinale; 
    cout<<"\n\n\tBenvenuto, questo programma ti consente di convertire la parte\n" 
      "\tdecimale di un numero in sistema binario. Per iniziare, inserisci un numero: "; 
    cin>>Numero; 
    cout<<Numero<<" Numero\n\n"<<endl; // 
    ParteIntera=Numero; //casting implicito, in modo da poter isolare esclusivamente la parte decimale del numero inserito. 
    cout<<ParteIntera<<" ParteIntera\n\n"<<endl; // 
    ParteDecimale=Numero-ParteIntera; //Isolamento della parte decimale 
    cout<<ParteDecimale<<" ParteDecimale\n\n"<<endl; // 
    Controllo2=ParteDecimale; 

do{ 
    ParteDecimale=ParteDecimale*2; //Moltiplicazione della parte decimale per due 
    cout<<"\n\nParteDecimaleCiclo "<<ParteDecimale<<endl; // 



    if(ParteDecimale<1){ 
     NumeroFinale=NumeroFinale+Var0; 
     cout<<"\n\nNumeroFinaleMin1 "<<NumeroFinale<<endl; // 
    } 
    else{ 
     NumeroFinale=NumeroFinale+Var1; 
     cout<<NumeroFinale<<" NumeroFinaleMagg1\n\n"<<endl; // 
     ParteDecimale-=1; 
     cout<<ParteDecimale<<" ParteDecimaleMagg1\n\n"<<endl; 
    } 
    Controllo=ParteDecimale; 
    cout<< "\n\nCONTROLLO " <<Controllo; // 
    cout<<"\n\nNUMERO "<<Numero; // 
    } 
    while(Controllo!=Controllo2); 

cout<<NumeroFinale<<endl; 
} 

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

Например:

-I вход «12,2», магазин программа номер в переменной «ParteIntera» (который является INT, я делаю, что для удаления дробной части);

-Он хранит «Numero-ParteIntera» внутри «ParteDecimale» (Numero - это ввод, введенный мной в начале), и в этот момент «ParteDecimale» равен 0,2;

-Значение «ParteDecimale» хранится внутри «Controllo2»;

-Начинается цикл Do-While, который в основном выполняет «ParteDecimale * 2», поэтому это «0,4»;

-It проверяет, является ли ParteDecimale больше или меньше 1, и на основании этого он выполняет инструкции внутри операторов if;

-Значение «ParteDecimale», которое получается из оператора if, затем сохраняется внутри «Controllo» (в этом случае оно равно 0,4);

Состояние проверено, в этом случае «Controllo» и «Controllo2» все еще отличаются, поэтому цикл начинается;

В определенный момент ParteDecimale будет равен 0,6, поэтому при запуске цикла ParteDecimale станет 1.2 (0.6 * 2). Поскольку он больше 1, выполняется инструкция else, а ParteDecimale - 0.2 (1.2-1). На данный момент «Controllo» и «Controllo2» равны 0,2, поэтому условие внутри do-while больше не выполняется, и цикл должен заканчиваться.

НО

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

Спасибо за прочтение этого очень длинного сообщения, любая помощь приветствуется, и извините за мой плохой английский!

+4

Правильный инструмент для решения таких проблем - ваш отладчик. Перед тем, как просить о переполнении стека, вы должны пропустить свой код по очереди *. Для получения дополнительной информации, пожалуйста, прочтите [Как отлаживать небольшие программы (Эрик Липперт)] (https://ericlippert.com/2014/03/05/how-to-debug-small-programs/). Как минимум, вы должны \ [изменить] ваш вопрос, чтобы включить пример [Минимальный, полный и проверенный] (http://stackoverflow.com/help/mcve), который воспроизводит вашу проблему, а также замечания, сделанные вами в отладчик. –

+6

Никогда не сравнивайте '==' два значения с плавающей запятой. Их n-й десятичный знак, вероятно, будет отличаться. Попытайтесь использовать дельта эпсилона для такой вещи. – Ceros

ответ

5

Выполнить этот фрагмент

float value = 2 * 0.3; 

if (value == 0.6) 
    std::cout << "Exactly" << std::endl; 
else 
    std::cout << "Ooops" << std::endl; 

И вы увидите, что на выходе что-то вроде

0.600000024 

Try использовать эпсилон-дельта-логики, а не точное значение

if (value >= 0.6 && value <= 0.6001) 
+0

Спасибо, как вы можете видеть, я довольно новичок в C++, и все же я не понимаю всех основ. Большое спасибо за то, что указали мне в правильном направлении! –

1

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

Таким образом, общий подход заключается в использовании некоторого значения допуска для сравнения, как в этой функции.

bool AreEqual(double value1, double value2) 
{ 
    double tolerance = 0.0001; 
    return fabs(value1 - value2) < tolerance; 
} 

В своем коде вы бы изменить состояние, как это:

while(!AreEqual(Controllo,Controllo2)); 
+1

Да, я абсолютно уверен, что Ceros прав, я не знал, что сравнение между двумя значениями с плавающей запятой - не лучшее решение. Спасибо за ваш ответ! –

1

Проблема здесь:

while(Controllo!=Controllo2); 

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

Они не представлены точно в карандаше на бумажных вычислениях, но мы склонны считать два значения равными, когда их первые десятичные числа равны. Компьютеры не работают таким образом. Controllo == Controllo2, только если две переменные имеют то же значение, что и их младшая значащая цифра. Компьютеры не делают никакого округления, если им не говорят делать это.

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

while(fabs(Controllo - Controllo2) > 0.0001); 

Если вы знаете заранее, как большие или малые являются значения Controllo и Controllo2 то вы можете выбрать постоянное значение и использовать его для сравнения. Это 0.0001 в моем примере выше, и он отлично работает при больших значениях Controllo и Controllo2. Вы должны использовать гораздо меньшие значения, если значения Controllo и Controllo2 имеют величину 0.001 или меньше.

Если вы не можете сказать заранее, насколько велика или как малы значения вы сравниваете, то вы можете проверить их относительную разницу:

while(fabs((Controllo - Controllo2)/Controllo2) > 0.0001); 

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

+0

Удивительное объяснение, большое вам спасибо! Я собираюсь попробовать все приведенные здесь примеры, пока не найду ту, которая подходит для того, что я пытаюсь сделать. Большое спасибо за ваше время! –

+0

Извините, я просто попытался реализовать свой код точно так же, как вы его написали, но он работает только для значения, которое кратно 0,2. Я имею в виду, если я вставляю 0.1 или 0.3, поскольку вход не работает, но он работает с 0.2; 0.4; 0.6; 0.8 и так далее! Не могли бы вы объяснить мне почему? Я пытался немного поиграть со значениями, но я просто не могу это получить –

Смежные вопросы