Как сделать обработку исключении на простом примере?

Форум для изучающих FPC и их учителей.

Модератор: Модераторы

Как сделать обработку исключении на простом примере?

Сообщение viktor96 » 26.05.2015 21:42:23

Привет всем! Решил (ну как решил, в универе д\з такое было, основную часть сделал, но меня затянуло) написать простенькую программу для вырисовки графиков используя модуль Graph. Всё бы хорошо, но ударила моча в голову сделать прогу, как говорится "универсальной", ну или хотя бы очень удобной для вывода на экран различных графиков. Вся соль в том что, у функции, как мы все знаем, есть область допустимых значении за границы которых нельзя выходить иначе поиск функции по Х, лежащей где-то вне ОДЗ, чревато стонами дебагера о Великой Ошибке Изображение и последующим неприятным обрушением программы :(. И тут встал вопрос, как пробежаться по всем значениям X, не вызвав это исключение во время вычислении. К примеру я беру функцию y=ln(x). Как мы знаем ОДЗ в данном случае при Х>0, но что если я не хочу писать так:
Код: Выделить всё
function F(x:real):real;      //y=ln(x);
   begin
   if x>0 then   F:=ln(x)*step_y;      //step_y - коэффициент для масштабирования графика по Y   
   end;

Ведь в любой момент времени и места, я могу взять исходник и поменять функцию на другую (простите но в самой программе ввод необходимой функции я ещё не научился делать, не злитесь :wink: ):
Код: Выделить всё
function F(x:real):real;      //y=sqrt(x^5)/(3x^2+12x-3)
   begin
   if x ??? then                                      // Ну и что за ОДЗ ?
   F:=(sqrt(x*x*x*x*x)/(3*(x*x))+(12*x)-3)*step_y;         
   end;

В таком случае, мне нужно искать ОДЗ и писать его в условие, когда я всего навсего могу сделать так:
Код: Выделить всё
function F(x:real):real;      //y=sqrt(x^5)/(3x^2+12x-3)
   begin
   try
      F:=(sqrt(x*x*x*x*x)/(3*(x*x))+(12*x)-3)*step_y;         
   except
      F:=0;
   end;
end;


И тут я попадаю впросак: при выполнении программы по-прежнему выводиться сообщение дебагера о ошибке мол проект вызвал класс исключении: "External: SIGFPE" в строчке F:=(sqrt(x*x*x*x*x)/(3*(x*x))+(12*x)-3)*step_y;
Помогите пожалуйста добрые люди, что я делаю не так? Почему сигнал не перехватывается обработчиком исключении? Есть ли путь обойти эту проблему с ОДЗ без использования танцев с бубном и муторных его поисков вручную? Заранее благодарю! :roll: P.S. Я программирую сейчас только на Pascal и то на уровне плинтуса, так что если я буду что-то не понимать, отнеситесь к этому с понимаем. Спасибо :wink:
viktor96
незнакомец
 
Сообщения: 3
Зарегистрирован: 26.05.2015 20:25:32

Re: Как сделать обработку исключении на простом примере?

Сообщение runewalsh » 26.05.2015 23:08:16

viktor96 писал(а):простите но в самой программе ввод необходимой функции я ещё не научился делать

Ничего, это не такая уж тривиальная задача, тем более для новичка.
Почему с сигналами так происходит, не скажу (проверил — у меня except работает, правда, ловит что-то непонятное с RaiseList[0].FObject = nil, т. е. казавшийся универсальным блок on System.TObject do этого не поймает. Dafuqisdat. Первый раз вижу), но вот что ты можешь сделать.

Подключи модуль SysUtils — он (вроде как) устанавливает обработчики, преобразующие ошибки времени выполнения в исключения языка.

-ИЛИ- (или «и»)

Все x86 используют контрольное слово FPU. Оно отвечает, помимо прочего, за то, какие исключения вычислений с плавающей точкой будут игнорироваться и вместо этого, когда применимо, порождать значения ±Inf (±x / 0.0, x > 0) или NaN (0.0 / 0.0; sqrt(x), x < 0; etc.). Конкретно это делают его биты с 0-го по 5-й:
5: PM / Precision (inexact result) mask
4: UM / Underflow mask
3: OM / Overflow mask
2: ZM / Zero-divide mask
1: DM / Denormalized operand mask
0: IM / Invalid operation mask

Не знаю, какие выставлены по умолчанию, но от греха подальше можешь замаскировать их все: Set8087CW(Get8087CW or %111111). После этого тебе придётся проверять результат на бесконечность и NaN. Проверять на NaN сравнением нельзя, потому что NaN'ы не равны сами себе. Стандартный модуль math (кстати, тянет за собой и SysUtils) предоставляет для этих целей функции IsInfinite и IsNaN. Шаманство с контрольным словом с его помощью тоже записывается более читаемым SetExceptionMask([exInvalidOp .. exPrecision]).
Аватара пользователя
runewalsh
энтузиаст
 
Сообщения: 578
Зарегистрирован: 27.04.2010 00:15:25

Re: Как сделать обработку исключении на простом примере?

Сообщение Sergei I. Gorelkin » 26.05.2015 23:44:01

При выполнении программы с отладчиком (из Lazarus) все исключения по умолчанию вначале "вываливаются" в отладчик и только после команды "продолжить выполнение" обрабатываются (ну или как повезет) самой программой. При исполении программы без отладчика этого не будет. Поведение отладчика тоже настраивается для каждого типа исключений - можно указать, чтобы на SIGFPE не реагировал.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1395
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Как сделать обработку исключении на простом примере?

Сообщение viktor96 » 27.05.2015 00:31:06

runewalsh писал(а):Не знаю, какие выставлены по умолчанию, но от греха подальше можешь замаскировать их все: Set8087CW(Get8087CW or %111111)

Спасибо большое за помощь! Программа замечательно сработала, нарисовав график без запинок. Изображение
Код: Выделить всё
function F(x:real):real;      //y=ln(x)
   begin
        Set8087CW(%111111);
      F:=(ln(x))*step_y;
end; 

К сожалению не до конца понимаю как читать параметр "%111111", как я понял он имеет тип word, но с таким типом я ещё не знаком:( Однако заметил что если изменить первую и последнюю единицу на нули, то неизбежно возвращается ошибка. Остальные значения никак не влияют на работу... Кстати, по неясным мне причинам, проверка на бесконечность не понадобилась, как и на NaN, график от этого не потерял стройности, хотя конечно это наверно не хорошо что оставил это так. Ещё раз спасибо за ваш отзыв!
UPD: На всякий случай добавил:
Код: Выделить всё
uses SysUtils,Math;
............................................
function F(x:real):real;      //y=ln(x)
begin
      F:=ln(x)*step_y;
                if IsInfinite(F) or IsNaN(F) then F:=0;
end;


Добавлено спустя 6 минут 9 секунд:
Sergei I. Gorelkin писал(а): можно указать, чтобы на SIGFPE не реагировал.

Спасибо за помощь! Действительно, отключение отладчика дало мне право на обработку ошибок, только вот при добавлении в исключения SIGFPE, он почему то всё равно на него реагирует...Скорее всего я не правильно указал тип исключения. Но тем не менее график теперь прекрасно рисуется :)
viktor96
незнакомец
 
Сообщения: 3
Зарегистрирован: 26.05.2015 20:25:32

Re: Как сделать обработку исключении на простом примере?

Сообщение runewalsh » 27.05.2015 00:47:16

%единицыинули — синтаксис FPC для двоичных литералов (чисел в двоичной системе счисления).
OR любого числа с %111111 выставляет ему младшие 6 битов и не трогает остальные.

viktor96 писал(а):Однако заметил что если изменить первую и последнюю единицу на нули, то неизбежно возвращается ошибка.

Ну тк, слева направо:
runewalsh писал(а):5: PM / Precision (inexact result) mask
4: UM / Underflow mask
3: OM / Overflow mask
2: ZM / Zero-divide mask
1: DM / Denormalized operand mask
0: IM / Invalid operation mask


> Set8087CW(%111111);
А так не надо делать, хотя ты почти ничего и не сломал. В контрольном слове есть другие биты с другими функциями, которые ты этим вызовом зануляешь. Set8087CW(Get8087CW or %111111) же. И да, это достаточно сделать один раз в начале программы.
Аватара пользователя
runewalsh
энтузиаст
 
Сообщения: 578
Зарегистрирован: 27.04.2010 00:15:25

Re: Как сделать обработку исключении на простом примере?

Сообщение viktor96 » 27.05.2015 01:13:46

runewalsh писал(а):%единицыинули — синтаксис FPC для двоичных литералов (чисел в двоичной системе счисления).
OR любого числа с %111111 выставляет ему младшие 6 битов и не трогает остальные.

viktor96 писал(а):Однако заметил что если изменить первую и последнюю единицу на нули, то неизбежно возвращается ошибка.

Ну тк, слева направо:
runewalsh писал(а):5: PM / Precision (inexact result) mask
4: UM / Underflow mask
3: OM / Overflow mask
2: ZM / Zero-divide mask
1: DM / Denormalized operand mask
0: IM / Invalid operation mask


> Set8087CW(%111111);
А так не надо делать, хотя ты почти ничего и не сломал. В контрольном слове есть другие биты с другими функциями, которые ты этим вызовом зануляешь. Set8087CW(Get8087CW or %111111) же. И да, это достаточно сделать один раз в начале программы.


Да, теперь буду знать, программу исправил, сделал только один вызов в начале программы, теперь я чувствую что программу можно назвать почти универсальной для любого графика типа y=f(x). В дальнейшем займусь подробным изучением аппаратной части, что бы не наломать дров) Спасибо вам огромное!!)
Оставлю код своей программы здесь чтобы желающие могли им воспользоваться для примера. Можете её модернизировать, я разрешаю)
Код: Выделить всё
program GoodWorkGraph;
uses Graph,SysUtils,CRT,Math;
var
c:char;
x,y,y1,step_x,step_y:real;
CenterCordinatXY:array [0..1] of integer;
sizeXp,sizeXm,sizeYp,sizeYm:integer;
const
   gradient=40;
   x_max=639;
   y_max=479;
   l=10;
   speed=5;
{ ********************************************************************** }
procedure GraphCHK;
var Gd,Gm:smallint;
begin
Gd:=VGA;
Gm:=VGAHi;
InitGraph(Gd,Gm,'');
  if GraphResult <> grOk then
     begin
       write('Ошибка');
       halt(1);
     end;
end;
{ ********************************************************************** }
procedure SetCenter(var CnCrd:array of integer);   //Установка центра
var st_x,st_y:real;
begin
st_x:=(x_max+1)/(sizeXp+sizeXm);
st_y:=(y_max+1)/(sizeYp+sizeYm);
CnCrd[0]:=round(st_x*sizeXm);
CnCrd[1]:=y_max-round(st_y*sizeYm);
end;

procedure SetSize (var sXp,sXm,sYp,sYm:integer);   //Установка размера графика как в положительных так и отрицательных
begin                                    //значениях X и Y
write('set size X+: '); readln(sXp);
write('set size X-: '); readln(sXm);
write('set size Y+: '); readln(sYp);
write('set size Y-: '); readln(sYm);
end;

procedure DrawGrid;
var loop,metka,gipfu:integer;
begin
Line(0,CenterCordinatXY[1],x_max,CenterCordinatXY[1]);
Line(CenterCordinatXY[0],0,CenterCordinatXY[0],y_max);
step_x:=x_max/(sizeXp+sizeXm);
step_y:=y_max/(sizeYp+sizeYm);
{******************************************************************************}
MoveTo(CenterCordinatXY[0],CenterCordinatXY[1]);                  //обрисовка меток по X+
loop:=0; metka:=0;
   while GetX<x_max do
      begin
        if (step_x>=20) then
         begin
         if loop mod round(step_x) = 0 then
            begin
            Line(GetX,GetY+l,GetX,GetY-l);
            OutTextXY(GetX-l,GetY+2*l,IntToStr(metka));
            inc(metka);
            end;
         end
      else
         begin
         gipfu:=round(gradient/step_x);
         if loop mod round(step_x*gipfu) = 0 then
            begin
            Line(GetX,GetY+l,GetX,GetY-l);
            OutTextXY(GetX-l,GetY+2*l,IntToStr(metka));
            inc(metka,gipfu);
            end;
         end;
      inc(loop);
      MoveTo(GetX+1,GetY);
      end;
{***************************************************************************************************************}      
MoveTo(CenterCordinatXY[0],CenterCordinatXY[1]);                        //обрисовка меток по X-
loop:=0; metka:=0;
   while GetX>0 do
      begin
        if (step_x>=20) then
         begin
         if loop mod round(step_x) = 0 then
            begin
            Line(GetX,GetY+l,GetX,GetY-l);
            OutTextXY(GetX-l,GetY+2*l,IntToStr(-metka));
            inc(metka);
            end;
         end
      else
         begin
         gipfu:=round(gradient/step_x);
         if loop mod round(step_x*gipfu) = 0 then
            begin
            Line(GetX,GetY+l,GetX,GetY-l);
            OutTextXY(GetX-l,GetY+2*l,IntToStr(-metka));
            inc(metka,gipfu);
            end;
         end;
      inc(loop);
      MoveTo(GetX-1,GetY);
      end;
{***************************************************************************************************************}
MoveTo(CenterCordinatXY[0],CenterCordinatXY[1]);                        //обрисовка меток по Y+
loop:=1; if (step_y>=20) then metka:=1 else metka:=round(gradient/step_y);
   while GetY<y_max do
      begin
        if (step_y>=20) then
         begin
         if loop mod round(step_y) = 0 then
            begin      
            Line(GetX-l,GetY,GetX+l,GetY);
            OutTextXY(GetX+l+2,GetY-2,IntToStr(metka));
            inc(metka);
            end;
         end
      else
         begin
         gipfu:=round(gradient/step_y);
         if loop mod round(step_y*gipfu) = 0 then
            begin
            Line(GetX-l,GetY,GetX+l,GetY);
            OutTextXY(GetX+l+2,GetY-2,IntToStr(metka));
            inc(metka,gipfu);
            end;
         end;
      inc(loop);
      MoveTo(GetX,GetY-1);
      end;
{***************************************************************************************************************}
MoveTo(CenterCordinatXY[0],CenterCordinatXY[1]);                        //обрисовка меток по Y-
loop:=1; if (step_y>=20) then metka:=1 else metka:=round(gradient/step_y);
   while GetY>0 do
      begin
        if (step_y>=20) then
         begin
         if loop mod round(step_y) = 0 then
            begin      
            Line(GetX-l,GetY,GetX+l,GetY);
            OutTextXY(GetX+l+2,GetY-2,IntToStr(-metka));
            inc(metka);
            end;
         end
      else
         begin
         gipfu:=round(gradient/step_y);
         if loop mod round(step_y*gipfu) = 0 then
            begin
            Line(GetX-l,GetY,GetX+l,GetY);
            OutTextXY(GetX+l+2,GetY-2,IntToStr(-metka));
            inc(metka,gipfu);
            end;
         end;
      inc(loop);
      MoveTo(GetX,GetY+1);
      end;      
end;
{****************************************************************************}

function F(x:real):real;      //y=ln(x+5)
begin
   F:=ln(x+5)*step_y;
end;

procedure DrawFuntcion;            //Обрисовка графика
var
loop:integer;
begin;
x:=-CenterCordinatXY[0]/step_x;y:=0;y1:=0; loop:=-CenterCordinatXY[0];
while loop<x_max-CenterCordinatXY[0] do
   begin
         y:=F(x);
         y1:=F(x+(1/step_x));
                         if not(IsInfinite(y) or IsNaN(y)) then
                         if not (IsInfinite(y1) or  IsNaN(y1)) then
         Line(loop+CenterCordinatXY[0],CenterCordinatXY[1]-round(y),loop+1+CenterCordinatXY[0],CenterCordinatXY[1]-round(y1));
                        x:=x+(1/step_x);
         inc(loop);   
   end;
end;



procedure MoveLeft;
begin
CenterCordinatXY[0]:=CenterCordinatXY[0]+speed;
ClearDevice;
DrawGrid;
DrawFuntcion;
end;

procedure MoveRight;
begin
CenterCordinatXY[0]:=CenterCordinatXY[0]-speed;
ClearDevice;
DrawGrid;
DrawFuntcion;
end;

procedure MoveUp;
begin
CenterCordinatXY[1]:=CenterCordinatXY[1]+speed;
ClearDevice;
DrawGrid;
DrawFuntcion;
end;

procedure MoveDown;
begin
CenterCordinatXY[1]:=CenterCordinatXY[1]-speed;
ClearDevice;
DrawGrid;
DrawFuntcion;
end;


begin
Set8087CW(Get8087CW or %111111);
SetSize(sizeXp,sizeXm,sizeYp,sizeYm);
SetCenter(CenterCordinatXY);
GraphCHK;
DrawGrid;
DrawFuntcion;
repeat
c:=ReadKey;
if c=#75 then MoveLeft;
if c=#80 then MoveDown;
if c=#77 then MoveRight;
if c=#72 then MoveUp;
until c=#27;
CloseGraph;
end.
viktor96
незнакомец
 
Сообщения: 3
Зарегистрирован: 26.05.2015 20:25:32


Вернуться в Обучение Free Pascal

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 8

Рейтинг@Mail.ru