1. Заголовок или название темы должно быть информативным 2. Все тексты программ должны помещаться в теги [CODE=asm] [/CODE] 3. Прежде чем задавать вопрос, см. "FAQ",если там не нашли ответа, воспользуйтесь ПОИСКОМ, возможно, такую задачу уже решали! 4. Не предлагайте свои решения на других языках, кроме Ассемблера. Исключение только с согласия модератора. 5. НЕ используйте форум для личного общения! Все, что не относиться к обсуждению темы - на PM! 6. Проверяйте программы перед тем, как выложить их на форум!!
Ошибка в программе вычисления интеграла, метод трапеций
пишу программу вычисления интеграла функции 1/ln x методом трапеций, для начала взял n=20 (затем буду увеличивать). Файл .exe создан, но выдает ошибку и вылетает. При просмотре в дебаггере вижу, что еще на этапе вычисления шага в регистре ST0 оказывается отрицательная величина, еще через пару шагов вылетает. В чем тут дело?
Заранее спасибо.
.486p .model small .stack 100h .data a dw 2 b dw 5 x dq 2 n dw 20 h dq ? y dq ?
.code main proc mov ax, @data mov ds, ax finit fild b fisub a fidiv n fst h ;вычисляем шаг h
fld1 fild b fyl2x fldln2 fmul fld1 fdiv fADD y fild b fdivr st(1), st ;вычисляем (ln a + ln b)/2
cycl: fadd y fst y
fild x fadd h fst x ;производим приращение х и каждый раз сохраняем ficom b je mult_ ;пока не равно b(=5),продолжаем
fld x fyl2x fldln2 fmul fld1 fdiv ; loop cycl ;
mult_: fld y ; fld h ; fmul ;когда х достиг значения 5, перемножаем на h
exit: mov ax, 4c00h ; int 21h ; main endp end main
Цикл ему кажется слишком длинным: на инструкции loop outer_loop выдает "Relative jump out of range by 22h bytes". И почему-то стал ругаться на невинную команду fstsw ax: "Illegal immediate".
Ты б за структурой следил, и вторую процедуру записывал бы после того, как первая закончится - было бы меньше неожиданностей. Насчет цикла - не подтверждается, код прекрасно собирается (помнят еще руки-то Цикл рассчитан до байта, добавляем еще одну инструкцию - 2 лишних байта для LOOP-а). На скрине - результат компиляции и запуска вот такого кода:
.286p .model small .stack 100h .data a dw 2 b dw 5 x dq 2.0 n dw 0 d dw 20 h dq ? y dq ? NL db 0Dh, 0Ah, "$"
.code
CalcF macro ; На вершине стека должен быть параметр fld1 fld st(1) fyl2x fldln2 fmul fld1 fdivr
endm
iCalcF macro param fld1 fild param
fyl2x fldln2 fmul fld1 fdivr
endm
CalcH macro
mov cx, n add cx, d mov n, cx dec cx ; вычисляем шаг fild b fisub a fidiv n fst h ; Сохраняем шаг
endm
main proc
mov ax, @data mov ds, ax
; начинаем цикл по увеличению N mov cx, 3 ; Будут рассчитаны значения при N = 20, 40, 60 outer_loop: finit ; выносим инициализацию сюда push cx CalcH
iCalcF b ; 1 / ln b fstp y iCalcF a ; 1 / ln a fadd y ; Складываем fdiv x ; Делим сумму на 2 fstp y ; Сохраняем начальное приближение
; Теперь внутренний цикл - вычисляем интеграл fld x ; Будем держать X в стеке сопроцессора inner_loop: fadd h ; X <- X + h CalcF fadd y fstp y ; Сохраняем измененное значение интеграла loop inner_loop
fld y fmul h ; Домножаем на h - получаем искомое значение call outfloat
pop cx loop outer_loop;
exit: mov ax, 4c00h ; int 21h ; main endp
; Требуется директива .286C или выше. outfloat proc near push ax push cx push dx ; Формируем кадр стэка, чтобы хранить в нём десятку ; и ещё какую-нибудь цифру. push bp mov bp, sp push 10 push 0 ; Проверяем число на знак, и если оно отрицательное, ftst fstsw ax sahf jnc @of1 ; то выводим минус mov ah, 02h mov dl, '-' int 21h ; и оставляем модуль числа. fchs ; Пояснение далее пойдёт на примере. ; ST(0) ST(1) ST(2) ST(3) ... ; Отделим целую часть от дробной. ; 73.25 ... что-то не наше @of1: fld1 ; 1 73.25 ... fld st(1) ; 73.25 1 73.25 ... ; Остаток от деления на единицу даст дробную часть. fprem ; 0.25 1 73.25 ... ; Если вычесть её из исходного числа, получится целая часть. fsub st(2), st ; 0.25 1 73 ... fxch st(2) ; 73 1 0.25 ... ; Сначала поработаем с целой частью. Считать количество цифр будем в CX. xor cx, cx ; Поделим целую часть на десять, @of2: fidiv word ptr [bp - 2] ; 7.3 1 0.25 ... fxch st(1) ; 1 7.3 0.25 ... fld st(1) ; 7.3 1 7.3 0.25 ... ; отделим дробную часть - очередную справа цифру целой части исходного числа,- fprem ; 0.3 1 7.3 0.25 ... ; от чатсного оставим только целую часть fsub st(2), st ; 0.3 1 7 0.25 ... ; и сохраним цифру fimul word ptr [bp - 2] ; 3 1 7 0.25 ... fistp word ptr [bp - 4] ; 1 7 0.25 ... inc cx ; в стэке. push word ptr [bp - 4] fxch st(1) ; 7 1 0.25 ... ; Так будем повторять, пока от целой части не останется ноль. ftst fstsw ax sahf jnz short @of2 ; Теперь выведем её. mov ah, 02h @of3: pop dx ; Вытаскиваем очередную цифру, переводим её в символ и выводим. add dl, 30h int 21h ; И так, пока не выведем все цифры. loop @of3 ; 0 1 0.25 ... ; Итак, теперь возьмёмся за дробную часть, для начала проверив её существование. fstp st(0) ; 1 0.25 ... fxch st(1) ; 0.25 1 ... ftst fstsw ax sahf jz short @of5 ; Если она всё-таки ненулевая, выведем точку mov ah, 02h mov dl, '.' int 21h ; и не более шести цифр дробной части. mov cx, 6 ; Помножим дрообную часть на десять, @of4: fimul word ptr [bp - 2] ; 2.5 1 ... fxch st(1) ; 1 2.5 ... fld st(1) ; 2.5 1 2.5 ... ; отделим целую часть - очередную слева цифру дробной части исходного числа,- fprem ; 0.5 1 2.5 ... ; оставим от произведения лишь дробную часть, fsub st(2), st ; 0.5 1 2 ... fxch st(2) ; 2 1 0.5 ... ; сохраним полученную цифру во временной ячейке fistp word ptr [bp - 4] ; 1 0.5 ... ; и сразу выведем. mov ah, 02h mov dl, [bp - 4] add dl, 30h int 21h ; Теперь, если остаток дробной части ненулевой fxch st(1) ; 0.5 1 ... ftst fstsw ax sahf ; и мы вывели менее шести цифр, продолжим. loopnz @of4 ; 0 1 ... ; Итак, число выведено. Осталось убрать мусор из стэка. @of5: fstp st(0) ; 1 ... fstp st(0) ; ... ; Точнее, стэков. leave
mov ah, 9 mov dx, offset NL int 21h
pop dx pop cx pop ax ret outfloat endp
end main
Сообщение отредактировано: IUnknown - 12.09.2012 23:11