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


Проанализировав исходный код, можно понять, как переплетаются графическая и вычислительная части проекта.
Решение кубического уравнения происходит в отдельной функции, принимающей на входе коэффициенты уравнения.
// Функция cubic(...) предназначена для решения кубического уравнения
//
// a3*x^3 + a2*x^2 + a1*x + a0 = 0
//
int
cubic(
// НА ВХОДЕ:
double a0,double a1,double a2,double a3, // коэффициенты уравнения
// НА ВЫХОДЕ:
double &x1, // Всегда первый действительный корень
double &x2, // Если уравнение имеет 3 действительных корня -
// это второй корень уравнения;
// в противном случае x2 - это действительная часть
// комплексно-сопряженной пары корней
double &x3, // Если уравнение имеет 3 действительных корня -
// это третий корень уравнения;
// в противном случае x3 - это модуль мнимой части
// комплексно-сопряженной пары корней
double eps
)
// ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ:
// * * *
// 0 - корни действительные (x1 = x1, x2 = x2, x3 = x3);
// 1 - один действительный и два комплексно сопряженных корня
// * * *
// (x1 = x1, x2 = x2 + x3*i, x3 = x2 - x3*i);
// -1 - это не кубическое уравнение, т.к. a3 == 0.
{
double z, f, A, B, C, D, x_prev;
int k_iter;
if (a3 == 0) return -1;
if (a3 < 0)
{
a3 = -a3;
a2 = -a2;
a1 = -a1;
a0 = -a0;
}
x_prev = x1 = a0/3;
k_iter = 0;
// Находим действительный корень x1 методом Ньютона:
for (;;)
{
f = (x1*(x1*(a3*x1 + a2)+a1)+a0);
if (fabs(f) < eps) break;
z = x1*(3*a3*x1+2*a2)+a1;
if (fabs(z) <= 2.2e-16)
{
x1 *= 2;
if (x1 == 0) x1 += 1;
}
x1 = x1 - f/z;
if (x_prev == x1)
{
k_iter++;
if (k_iter > 5)
{
k_iter = 0;
eps *= 5;
}
}
x_prev = x1;
}
A = a3;
B = a2+A*x1;
C = a1+B*x1;
// A,B,C - коэффициенты квадратного уравнения
D = B*B-4*A*C; // его дискриминант
A *= 2;
if (D >= 0)
{
// два остальных корня кубического уравнения - действительные
D = sqrt(D);
x2 = (-B-D)/A;
x3 = (-B+D)/A;
return 0;
}
D = sqrt(fabs(D));
x2 = -B/A;
x3 = fabs(D/A);
return 1;
}
Отличительной особенностью в данном проекте является работа со шрифтом: задание его параметров и дальнейшее использование для вывода текста в многострочное текстовое поле ListBox
font.CreateFont(
-10, // nHeight
0, // nWidth
0, // nEscapement
0, // nOrientation
FW_NORMAL, // nWeight
FALSE, // bItalic
FALSE, // bUnderline
0, // cStrikeOut
RUSSIAN_CHARSET, // nCharSet
OUT_DEFAULT_PRECIS, // nOutPrecision
CLIP_DEFAULT_PRECIS, // nClipPrecision
DEFAULT_QUALITY, // nQuality
DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
"Condensed"); // lpszFacename
((CListBox *)GetDlgItem(IDC_LIST1))->SetFont(&font);
Также интересна функция rfp(). Эта функция работает с входными данным, проверяет их достоверность, преобразует из строк в числа. После получения решения в строке int ier = cubic(a0,a1,a2,a3,x1,x2,x3,1e-6); формирует строку нужного формата и преобразует числа в строку. Указатель типа void * необходим для указания на определенную область памяти, в которой размещены данные неопределенного типа. Проще говоря, можно только запомнить область памяти. Но через подобный указатель нельзя вызывать никакие методы и члены класса.
UINT rfp(void *)
{
CString s;
double a3,a2,a1,a0;
double x1,x2,x3;
double pr;
unsigned long kp, i;
stopper = FALSE;
// Осуществляем проверку:
int ier9 = _stscanf(o->m_pr,_T("%le"),&pr);
int ier10= _stscanf(o->m_kp,_T("%lu"),&kp);
int ier3 = _stscanf(o->m_a3,_T("%le"),&a3);
int ier2 = _stscanf(o->m_a2,_T("%le"),&a2);
int ier1 = _stscanf(o->m_a1,_T("%le"),&a1);
int ier0 = _stscanf(o->m_a0,_T("%le"),&a0);
int ier = ((CListBox *)o->GetDlgItem(IDC_LIST1))->InitStorage(100000, 100);
//ASSERT(n != LB_ERRSPACE);
((CListBox *)o->GetDlgItem(IDC_LIST1))->ResetContent();
// В ier0, ier1 , ... - коды возврата функции _stscanf(...)
// Если ier0 == 0 => чтение прошло неудачно.
if ( !ier0 || !ier1 || !ier2 || !ier3 || !ier9 || !ier10 )
{
AfxMessageBox(_T("Проверьте поля ввода !"));
return 0;
}
//s.Format("%lu",kp);
//AfxMessageBox(s);
for (i=0;i<kp;i++)
{
if (stopper) break;
a0=(1+(pr/100)*rand()/(double)RAND_MAX)*a0;
a1=(1+(pr/100)*rand()/(double)RAND_MAX)*a1;
a2=(1+(pr/100)*rand()/(double)RAND_MAX)*a2;
a3=(1+(pr/100)*rand()/(double)RAND_MAX)*a3;
// Решаем уравнение
int ier = cubic(a0,a1,a2,a3,x1,x2,x3,1e-6);
if (ier == 0) // Корни все действительные
{
s.Format(_T("%lg %lg %lg"),x1,x2,x3);
}
else // Один корень дейстивтельный, два других - комплексно-сопряженные
{
s.Format(_T("%lg %lg+(%lg)*i %lg+(%lg)*i"),x1,x2,x3,x2,-x3);
} // %6.3lf
ier = ((CListBox *)o->GetDlgItem(IDC_LIST1))->AddString(s);
if (ier == LB_ERR || ier == LB_ERRSPACE)
{
AfxMessageBox("Error");
break;
}
s.Format("%lu",kp);
o->SetDlgItemText(IDC_STATIC_TMP,s);
}
s.Format("%lu",i);
AfxMessageBox(s);
return 0;
}
При нажатии кнопки Button5 меняется только заголовок кнопки.
void CZ1Dlg::OnButton5()
{
if (m_switch)
{
GetDlgItem(IDC_BUTTON5)->SetWindowText(_T("Пауза"));
}
else
{
GetDlgItem(IDC_BUTTON5)->SetWindowText(_T("Продолжить"));
}
m_switch = !m_switch;
}
Перед закрытием, если поток существует, его надо приостановить:
if (m_potok) m_potok->SuspendThread();
Запись результатов итераций в текстовый файл.
void CZ1Dlg::OnButton4()
{
FILE *f = fopen("tmp.txt","a");
int ier = 10000, i;
CString s;
int n;
n = ((CListBox *)GetDlgItem(IDC_LIST1))->GetCount();
for (i = 0; i < n; i++)
{
// Получили очередную строку из listbox'а:
((CListBox *)GetDlgItem(IDC_LIST1))->GetText(i,s);
fprintf(f,"%s\n",(const char *)s);
}
fclose(f);
}