Форум «Всё о Паскале» _ Задачи _ Как преобразовать Integer в Real
Автор: Xelix 24.12.2006 0:52
Код
Program LR10; Uses CRT,graph; Procedure GrOnScr; Var y,x:real; expo:real; i,i2:integer; yscr:integer; dr,rm:integer; Begin dr:=detect; InitGraph(dr,rm,'D:\TP7\BIN'); MoveTo(0,240); LineTo(640,240); Moveto(320,0); LineTo(320,480); MoveTo(0,240); SetLineStyle(0,Solidln,1); yscr:=0; i:=1; Repeat [b]yscr:=Round((7*sqr(i)*(exp(-5*i)))*480/640+240);[/b] If i<=640 Then LineTo(i,yscr); PutPixel(Random(640),Random(480),Random(5)); Delay(500); PutPixel(Random(640),Random(480),Random(5)); i:=i+1; Until keypressed; End; Begin GrOnScr; ReadKey; End.
В выделенной строке компилятор выдаёт ошибку floating point overflow. Что делать?
Автор: polic 24.12.2006 0:57
какая из них выделенная?
Автор: Xelix 24.12.2006 1:01
yscr:=Round((7*sqr(i)*(exp(-5*i)))*480/640+240);
Автор: мисс_граффити 24.12.2006 1:34
дело не в переводе. у меня вылетает при i=18... причем пишет про overflow - то есть переполнение. почему оно возникает: exp(-90)=1/exp(90) exp(90)=1.22*10^39 многовато....
Автор: Xelix 24.12.2006 1:40
Так как исправить положение? Когда отключаю комментами функцию экспоненты всё работает. Работает и когда из экспоненты убираю i
Автор: мисс_граффити 24.12.2006 1:46
у функции exp и аргумент, и возвращаемое значение - real либо делай проверку и при больших х заменяй в формуле эту эксопоненту на 0, либо пиши свою функцию с более емким типом.
Автор: Гость 24.12.2006 1:53
НО тут же подэкспоненциальное выражение максимум выходит -3200! О каком переполнении reala может идти речь? Может можно как-то заменить именно эту функуцию через логарифм например? Лично мне кажется, что тут всё же паскаль ругается из-за того, что i-переменная типа интеджер. Так как же её перевести в real?
Автор: volvo 24.12.2006 1:55
Integer -> Real переводится автоматически, проблема не в этом... Попробуй поменять тип с Real на Double и первой строкой программы сделать {$N+}
Автор: мисс_граффити 24.12.2006 2:15
Цитата(Гость @ 23.12.2006 21:53)
Лично мне кажется, что тут всё же паскаль ругается из-за того, что i-переменная типа интеджер. Так как же её перевести в real?
Угу. Именно так и есть, конечно. Маленьких интеджеров (до 18) паскаль не боится, а потом пугается и решает в real не переводить - мало ли что.
А то, что вылетает на примерно 10^39, а граница диапазона real'а - 1.7*10^38 - совпадение!
Как ты думаешь, если процедура получает и возвращает результат типа real, внутри нее вычисления на каком типе ведутся?
Цитата
Попробуй поменять тип с Real на Double
Тип чего?
Автор: Xelix 24.12.2006 2:16
А если же в этой строке убрать минус перед 5, то ошибка другая: "Invalid floating poit operation"
Автор: volvo 24.12.2006 2:18
Цитата
НО тут же подэкспоненциальное выражение максимум выходит -3200!
Правда? А если подумать? И сделать вот так, например:
Program LR10; Uses CRT,graph; Procedure GrOnScr; Var y,x:real; expo:real; i,i2:integer; yscr:integer; Begin yscr:=0; i:=1; Repeat writeln('i = ', i); yscr:=Round((7*sqr(i)*(exp(-5*i)))*480/640+240); If i<=640 Then writeln('lineto', i, ' ', yscr); i:=i+1; Until keypressed; End; Begin GrOnScr; ReadKey; End.
, то при каком значении i программа вылетит? У меня получилось 2272!!! А значит, под экспонентой не -3200, а -11360... А теперь - небольшой эксперимент:
writeln(exp(-5 * 2271));
и смотрим на результат:
Цитата(Console)
3.85618613316889E-4932
Это при граничном значении, равном 3.4e-4932 для типа Extended!!! И что произойдет, если я еще чуть-чуть увеличу выражение под экспонентой? То, что и произошло у тебя, FP Overflow... Возражения?
Цитата
А если же в этой строке убрать минус перед 5
То значение, которое должно получиться в результате - не поместится в отводимое под тип место, и ... Результат ты видел ...
Автор: Xelix 24.12.2006 2:34
Хорошо, проблема ясна - мы легко переполняем тип. А теперь извечный русский вопрос - что делать?
Автор: мисс_граффити 24.12.2006 3:05
Цитата
У меня получилось 2272!!!
а у меня опять 18 (TP 7.0)
Гость, ты мои сообщения игнорируешь? Что ж, твое право.
Автор: volvo 24.12.2006 3:27
Цитата
а у меня опять 18 (TP 7.0)
У меня тоже TP70, только поддержка сопроцессора у меня подключена в опциях компилера, поэтому у меня программа использует гораздо более емкий тип Extended (даже если в программе и фигурирует Real), а у тебя - именно Real, что влияет на результат...
FPC, кстати, вообще не сбоит - даже при i > 20000
Цитата
что делать?
Перейти на Extended и сделать так:
Repeat yscr:=Round((7*sqr(i)*(exp(-5*i)))*480/640+240); If i<=640 Then LineTo(i,yscr) Else Break; { <--- !!! } PutPixel(Random(640),Random(480),Random(5)); Delay(500); PutPixel(Random(640),Random(480),Random(5)); i:=i+1; Until keypressed;
Автор: Xelix 24.12.2006 3:32
Я ничьи сообщения не игнорирую, а наоборот, читаю все советы. В любом случае, нашёл кое-какой выход из положения, чтобы график качественно расовался как надо.
Автор: мисс_граффити 24.12.2006 3:57
Цитата
FPC, кстати, вообще не сбоит - даже при i > 20000
делфи тоже... Для
y:=round(exp(-i));
(y: integer) прекрасно работает, а вот для
y:=round(exp(i));
вылетает при i=44. По-разному реализовано вычисление отрицательных степеней?
Не путай исчезающе малое число и очень большое... Если в первом случае FPC/Delphi просто поступают логично, заменяя число с порядком почти -5000 обыкновенным нулем, то в случае с порядком +5000 - каким бы "умным" не был компилятор - ему просто некуда засунуть полученное число, и программа вылетит с переполнением типа...
Автор: мисс_граффити 24.12.2006 4:54
это понятно. я о другом... про 44 написала просто для того, чтобы показать сопоставимость степеней, на которых происходит переполнение.
ну, грубо говоря, чтобы посчитать 5^-2 можно либо сначала посчитать 5^2 (вот здесь риск получить переполнение), а потом разделить на это единицу . А можно единицу 2 раза разделить на 5 (никакого переполнения). Получается, в разных компиляторах одно и то же реализовано по-разному... или в более "умных" вариантах просто вставлено условие типа если степень меньше скольки-то, то не заморачиваться с подсчетами и выдать 0?