Я подозреваю, что это может быть как-то связано с ошибками округления, так как я использую удваивания для контроля завершения цикла, но я хотел бы действительно знать, что происходит
#include<iostream>
using namespace std;
int main()
{
double h = 0.2; // stepsize
double t_0 = 1;
double t_n = 25;
double y_0 = 1; // initial conditiondouble t = t_0;
while(t < t_n)
{
cout << "t: " << t << endl;
cout << "(t < t_n): " << (t < t_n) << endl;
t += h;
}
}
Последние несколько строк вывода
t: 24.4
(t < t_n): 1
t: 24.6
(t < t_n): 1
t: 24.8
(t < t_n): 1
t: 25
(t < t_n): 1
Разве последнее утверждение не должно возвращать ложь? Т.е. не должен ли цикл завершаться при 24,8?
Причина этого не работает в том, что 0.2
не может быть представлен точно в float
потому что его дробная часть не является точной суммой отрицательных степеней двух. Если вы попробуете это с другим номером, скажем, 0.25
код будет работать, потому что 0.25
является 2^-2
,
Ты прав, double
не точный тип, и вы не можете ожидать точных результатов. (Типичным примером является то, что 0,1 + 0,1 + 0,1 не совпадает с 0,3; он может быть больше или меньше.) Если это возможно, предпочтите интегральную арифметику с фиксированной точкой:
for (int i = 10; i < 250; i += 2)
{
double t = i / 10.0;
std::cout << "t: " << t << "\n";
}
Как сказал @Kerrek SB, double
арифметика «не точна».
Точнее, 0.2
не может быть представлен именно с точки зрения double
, Это на самом деле что-то вроде 0.1999999...
, Потому что 0,2 равно 1/5, тогда как 1/5 — это бесконечная дробь в двоичном представлении. (Как и 1/3 — это бесконечная дробь в десятичном представлении).
вы можете использовать double с плавающим приведением в цикле, или вы можете создать свой собственный тип double с пользовательским < оператор
#include <iostream>
using namespace std;class _double
{
private :
double d ;
public :
_double(double d) { this->d = d ;}
double get() { return d ;}
_double &operator+=(_double &a)
{
this->d+=a.get();
return *this;
}
void display(ostream &out)
{
cout << this->d ;
}
};
bool operator<(_double &a,_double &b)
{
if ( (float ) a.get() < (float) b.get() )
return true ;
return false ;
}ostream& operator<<(ostream& out, _double & a)
{
a.display(out) ;
return out;
}
int main()
{
double h = 0.2; // stepsize
double t_0 = 24;
int t_n = 25.;
double y_0 = 1; // initial condition
_double d1(25);
_double d_t(24);
_double d_h(0.2);cout << endl << " =========== First Method ============== " << endl ;double t = t_0;
while((float) t<(float) t_n)
{
cout << "t: " << t<< endl;
cout << "(t < t_n): " << (t < t_n) << endl;
t += 0.2;
}cout << " out of loop t: " << t << endl;
cout << "out of loop -> (t < t_n): " << (t < t_n) << endl;
cout << " =========== Second Method ============== " << endl ;while( d_t< d1)
{
cout << "t: " << d_t<< endl;
cout << "(t < t_n): " << (d_t < d1) << endl;
d_t += d_h;
}cout << "out of loop t: " << t << endl;
cout << "out of loop -> (t < t_n): " << (t < t_n) << endl;
return 0;
}