прога под делфи(в универе тока оно стоит). При отладке выявился баг. procedure TForm1.Button1Click(Sender: TObject); // по нажатию на кнопку рисуется график функции // y=sqrt(x+10)+exp(x) var x:integer; p,pp:dword; a,b,c,d,nx,f,k,t,m,n,y:extended; begin a:=-10; //[a,b]-область определения b:=3; c:=-5; //[c,d]-область значений. d:=10; //t,k,m,n - вычисляемые смешения и маштабные множители. t:=(b-a)/(form1.ClientWidth); k:=(a*form1.ClientWidth)/(a-b); m:=form1.ClientHeight/(c-d); n:=(d*form1.ClientHeight)/(d-c); // оси координат: form1.Canvas.MoveTo(0,round(n)); form1.Canvas.lineto(form1.ClientWidth,round(n)); form1.Canvas.MoveTo(round(k),0); form1.Canvas.LineTo(round(k),form1.ClientHeight); // вычмсляем значение первой точки графика: x:=0; nx:=(x-k)*t; f:=sqrt(nx+10)+exp(nx); (***) f:=f*m+n; form1.Canvas.MoveTo(x,round(f)); form1.canvas.pen.color:=clred; /************************************************ так вот при B=3 а=-10 в строке (***) возникает ошибка типа неверное значение аргумента для ф-ции. при отладке(бряк на сроку с формулой и дальше в окне CPU) выяснилось что складывая числа 10 и -10 FPU получает число -8.****** и при попытке выполнить fsqrt FPU возбуждает исключения. при b=1 b b=3 такого нет. вопрос:какого хера оно так? **************************************************/ //дальнейший код для завершонности картины. for x:=0 to (form1.ClientWidth) do begin nx:=(x-k)*t; y:=nx+10; pp:=p; f:=sqrt(nx+10)+exp(nx); f:=f*m+n; p:=round(f); //form1.canvas.Pixels[x,p]:=clRed; if pp<=form1.ClientHeight then begin form1.canvas.LineTo(x,p); form1.Canvas.MoveTo(x,p); end; end; form1.Canvas.TextOut(0,round(n)+5,floattostr(a)); form1.Canvas.TextOut(form1.ClientWidth-15,round(n)+5, floattostr(b)); form1.Canvas.TextOut(round(k)+5,0,floattostr(d)); form1.Canvas.TextOut(round(k)+5,form1.ClientHeight-15, floattostr(c)); form1.Canvas.TextOut(round(k)+5,round(n)+5,'0'); end;
в стек он грузи 10 и -10(если вместо sqrt(nx+10) записать sqrt(nx+zz) где zz - extended со значением +10) а сложив командой fadd эти значения имеет -8.*** <font color="red]первое предупреждение за мат. изучаем Правила</font><!--color-->
Это не баг, а твое неумелое обращение с FPU Если у тебя на бумажке все сокращается и результат получается точно nx = 10, то в процессоре в результате делений умножений может получиться число 10 ± e, где е погрешность вычисления ~2^(-63) для extended. Самый простой совет - не используй переменные типа extended, где это не нужно. Extended используется внутри процессора для промежуточных расчетов, а переменные обычно используют double и при округлении extended до double эти мизерные ошибки исчезают. Это во-первых, а во-вторых из-за тех же мизерных погрешностей в ответсвенных ситуациях (деление на 0, sqrt,ln) нельзя сравнивать вещественные числа на точное равенство - нужно брать диапазон. В твоем случае, если не поможет double, то придется ввести проверку temp:=nx+10; if temp > 0 then f:=sqrt(temp) else f:=0; f:=f+exp(nx);
а то что в стеке после сложения чисел 10 и -10 получаестся -8.***? или это отладчик гонит? в окне фпу так и написано st(0)=10 st(1)=-10 после fadd st(0)=-8.***** т.е. погрешность больше?
За дельфи не ручаюсь, а вот OllyDbg по умолчанию все значения выводит в формате double (53 бита мантиссы, а не полные 64) и ты не первый кто сталкивался с этой "проблемой". В результате вместо 10+e ты видишь 10, но после вычитания эта разница проявляется и получаешь свои -8.*** Вообще все интеловские и амд-ишные мануалы рекомендуют работать с точностью double, но процессор при инициализии finit устанавливает точность вычислений extended и дельфи ее на double для твоей проги не изменяет. Нельзя говорить, что погрешность "большая", просто при огравниченной разрядности и точность ограничена. Например с какой точностью не записывай число 1/3=0.333333.. все равно при умножении на 3 ты получишь не 1.0, а 0.999999.. и только при округлении до меньшего числа разрядов получится 1.0. Поэтому основное назначение extended это промежуточные вычисления и в младших битах могут накапливаться ошибки, которые как правило исчезают при округлении к основному типу double. А вот для чего тебе extended с 64-битной мантиссой - я совершенно не могу понять, т.к. для стандартного разрешения экрана и single за глаза хватвет )) PS: Прежде чем писать очередной ответ в защиту своей версии о наличии бага в FPU, подумай - как с такими багами америкосы умудряются ракеты к марсу запускать )
вопщем спасибо, как понял проблема в том что икстендед отображается как дабл в отладчике. для чего extended? Я решил что по аналогии с рекомендацией использовать в 32х битном alu 32х битные одиночные переменные неплохо было бы использовать в 80ти битном fpu 80ти битные переменные. Ну и ещё все числа с плавающей точной в проге используются только для вычисления значений, а дальше все приводится к маштабу экрана(хотя конечно и singl хватит иначе график просто выродиться, но всеже). хмм а это не в тему но пример мне нравится: 0.9(9)=1 точно т.к. 0.9=3*0.3(3)=3*(1/3)=1 но при ограниченной точности это конечно не так, ведь понятия периодической дроби тогда нет.
В мануалах по оптимизации переменные типа 80-бит extended вообще не рекомедуют использовать, т.к. размер получается не кратным 32битам и читаются\пишутся в память такие переменные почти вдвое дольше чем 32 и 64 битные. Extended только для промежуточных вычислений или для специальных задач, где нужна повышенная точность - но там уж спецы все погрешности должны оценивать.