Помощь - Поиск - Пользователи - Календарь
Полная версия: Ошибка в программе вычисления интеграла
Форум «Всё о Паскале» > Современный Паскаль и другие языки > Ассемблер
Hey
Приветствую,

пишу программу вычисления интеграла функции 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

IUnknown
А ты в дебаггере посмотрел, что именно грузится в ST0 при выполнении
finit
fild b ; <--- вот этой строки
fisub a
fidiv n
fst h ;вычисляем шаг h
? Там явно не 5-ка, а 8397:
Нажмите для просмотра прикрепленного файла

Измени режим процессора на .286p, тогда значения станут подгружаться нормально, и по крайней мере h вычисляется правильно, дальше не проверял.
Hey
Спасибо, с тем фрагментом действительно пошло. Где-то дальше еще есть ошибки, сейчас буду смотреть.
TarasBer
А почему всё-таки fild пятёрки загрузил 8397?
Hey
Снова загвоздка: не принимает директиву loop, попросту ее пропускает. Это свойство моей версии ассемблера или режима 286р?

P.S. Виноват: забыл на счетчик СХ поставить. Правда, он почему-то считает только два цикла, затем обнуляется. Притом во втором цикле в x грузит сразу 4,8 вместо 2,15. ... прилагаю новый листинг.



.286p
.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
mov CX, [n]
dec CX
finit
fild b
fisub a
fidiv n
fst h  ;вычисляем шаг h 

finit
fld1
fild b
fyl2x
fldln2
fmul ; вычиcляем ln 5
fld1
fdivr ;вычиcляем 1/ln 5
fstp y

fldln2
fld1
fdivr ; вычисляем 1/ln2
fadd y
fstp y
fild a
fld y
fdivr ;вычисляем (1/ln a + 1/ln b)/2
fstp y

cycl:
finit
fld x 
fadd h
fst x ;производим приращение х и каждый раз сохраняем
fld1
fld x
fyl2x
fldln2
fmul
fld1 ;
fdivr ;
fadd y
fstp y 
loop cycl ; 

mult_: 
fld y 
fld h  
fmul   ;когда х достиг значения 5, перемножаем на h

IUnknown
Цитата
Это свойство моей версии ассемблера или режима 286р?
Это особенности цикла LOOP. Чтобы он работал, надо занести число повторений в CX. У тебя CX - нулевой. Похоже, тебе нужен не LOOP, а простой JMP, если ты из цикла уходишь по "je mult_"

Цитата
А почему всё-таки fild пятёрки загрузил 8397?
А кто его знает... В 286-ом режиме грузит нормально, выше - начинает придумывать что-то своё.
Hey
P.S. ТОчно, забыл на счетчик СХ поставить. Правда, он почему-то считает только два цикла, затем обнуляется. Притом во втором цикле в x грузит сразу 4,8 вместо 2,15. ... прилагаю новый листинг.



.286p
.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
mov CX, [n]
dec CX
finit
fild b
fisub a
fidiv n
fst h ;вычисляем шаг h

finit
fld1
fild b
fyl2x
fldln2
fmul ; вычиcляем ln 5
fld1
fdivr ;вычиcляем 1/ln 5
fstp y

fldln2
fld1
fdivr ; вычисляем 1/ln2
fadd y
fstp y
fild a
fld y
fdivr ;вычисляем (1/ln a + 1/ln b)/2
fstp y

cycl:
finit
fld x
fadd h
fst x ;производим приращение х и каждый раз сохраняем
fld1
fld x
fyl2x
fldln2
fmul
fld1 ;
fdivr ;
fadd y
fstp y
loop cycl ;

mult_:
fld y
fld h
fmul
IUnknown
С описанием
x dd 2.0
все итерации проходят (проверял 4 первых, дальше не хватило терпения, на выходе, после того как цикл завершился) имеем:
Hey
Цитата(IUnknown @ 9.09.2012 20:04) *

С описанием
x dd 2.0
все итерации проходят (проверял 4 первых, дальше не хватило терпения, на выходе, после того как цикл завершился) имеем:


Отлично, спасибо! Теперь начну увеличивать n, пока не достигну требуемой точности.
Hey
Опять вынужден обратиться: программу продолжил, но новый фрагмент он не видит и сразу переходит к выводу на экран прежнего значения для n=20. Привожу листинг без процедуры вывода на экран:


.286p
.model small 
.stack 100h
.data 
a dw 2
b dw 5
x dd 2.0
n dw 20 
d dw 20
h dq ?
y dq ?
y2 dq ?

.code 
main proc
mov ax, @data
mov ds, ax
m1: mov CX, [n]
dec CX
finit
fild b
fisub a
fidiv n
fst h  ;вычисляем шаг h 

finit
fld1
fild b
fyl2x
fldln2
fmul ; вычиcляем ln 5
fld1
fdivr ;вычиcляем 1/ln 5
fild n
ficom d
jg m2
fxch
fstp y


fldln2
fld1
fdivr ; вычисляем 1/ln2
fadd y
fstp y
fild a
fld y
fdivr ;вычисляем (1/ln a + 1/ln b)/2
fstp y

cycl:
finit
fld x 
fadd h
fst x ;производим приращение х и каждый раз 

сохраняем

fld1
fld x
fyl2x
fldln2
fmul
fld1 ;
fdivr ;
fadd y
fstp y 
loop cycl 

mult_: 
fld y 
fld h  
fmul   ;когда х = 5, перемножаем на h
fst y

;повторяем то же для n=40
finit
fild n
fiadd d
fistp n
jmp m1
m2:
fxch
fstp y2


fldln2
fld1
fdivr ; вычисляем 1/ln2
fadd y2
fstp y2
fild a
fld y2
fdivr ;вычисляем (1/ln a + 1/ln b)/2
fstp y2

cycl2:
finit
fld x 
fadd h
fst x ;производим приращение х и каждый раз 

сохраняем

fld1
fld x
fyl2x
fldln2
fmul
fld1 ;
fdivr ;
fadd y2
fstp y2 
loop cycl2 ; 

mult2_: 
fld y2 
fld h  
fmul   ;61 когда х = 5, перемножаем на h
fst y2

fsub y
fabs

IUnknown
Во-первых, с чего ты решил, что вот это:
Цитата
fild n
ficom d
jg m2
сработает так, как ты задумал? Не будет этого. Это тебе не процессорные команды сравнения, чтоб можно было использовать любые сравнения. Общий шаблон - такой:

    fcomp x ; сравниваем верхушку стека с числом X
    fstsw ax ; сохраняем слово состояния сопроцессора в AX
    sahf ; заталкиваем AH в регистр флагов

    ; а теперь - внимание:
    jp lbl_1 ; значения несравнимы
    jc lbl_2 ; st(0) < x
    jz lbl_3 ; st(0) = x
    ; Раз мы пришли сюда - значит st(0) > x
А во-вторых - зачем тебе все эти пляски с прыжками из одной части кода в другую? Сделай 2 нормальных вложенных цикла, изначально N присвой 0, и тут же его увеличивай на D, это будет внешний цикл. А внутренний - вычисление самого интеграла. Программа упростится донельзя (а если еще воспользоваться макросредствами ассемблера - у тебя же TASM, я правильно понимаю?) то все будет еще проще.
IUnknown
Вот первоначальный вариант (можно еще дорабатывать)
.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 ?

.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

main proc

	mov ax, @data
	mov ds, ax
	
	; начинаем цикл по увеличению N
	mov cx, 3	; Будут рассчитаны значения при N = 20, 40, 60
outer_loop:
	push cx
	
	mov cx, n
	add cx, d
	mov n, cx
	dec cx
	
	; вычисляем шаг
	finit
	fild b
	fisub a
	fidiv n
	fst h		; Сохраняем шаг
	
	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

; За кадром - реализация outfloat, распечатывающая содержимое 
; вершины стека сопроцессора и выбрасывающая распечатанное
; значение из стека

Hey
Однако серьезно! Буду пробовать. Признателен)

Кстати, почему я организовал дополнительные циклы, и ввел переменную у2: необходимо найти решение с определенной точностью (0,0001). Для этого придется сравнивать значения F для каждых "соседних" n. Соответственно, если для каждого последующего n я записываю значения f в одну и ту же переменную у, то значения при меньшем n теряются. Попробую что-нибудь придумать.
Hey
Цикл ему кажется слишком длинным: на инструкции loop outer_loop выдает "Relative jump out of range by 22h bytes". И почему-то стал ругаться на невинную команду fstsw ax: "Illegal immediate".

P.S. Я решил оформить макросом приращение n и вычисление h, прилагаю код целиком.




.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 ?

.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

calc_h macro
        mov cx, n
	add cx, d
	mov n, cx
	dec cx
	
	; вычисляем шаг
	finit
	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:
	push cx
	calc_h 
	
	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	
	call outfloat
	
	pop cx
	loop outer_loop;

	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

@of1:   fld1                           
        fld     st(1)                  
        fprem                         
        fsub    st(2), st              
        fxch    st(2)                 
 
        xor     cx, cx

@of2:   fidiv   word ptr [bp - 2]     
        fxch    st(1)                  
        fld     st(1)                  
        fprem                          
        fsub    st(2), st              
        fimul   word ptr [bp - 2]      
        fistp   word ptr [bp - 4]     
        inc     cx
        push    word ptr [bp - 4]
        fxch    st(1)                  
        ftst
        fstsw   ax
        sahf
        jnz     short @of2
; Теперь выведем её.
        mov     ah, 02h
@of3:   pop     dx
        add     dl, 30h
        int     21h
; И так, пока не выведем все цифры.
        loop    @of3                  
; Теперь за дробную часть, для начала ;проверив её существование.
        fstp    st(0)                 
        fxch    st(1)                  
        ftst
        fstsw   ax
        sahf
        jz      short @of5
; Если она  ненулевая, выведем точку
        mov     ah, 02h
        mov     dl, '.'
        int     21h
; и не более четырех цифр дробной части.
        mov     cx, 4
; Помножим дробную часть на десять,
@of4:   fimul   word ptr [bp - 2]      
        fxch    st(1)                  
        fld     st(1)                  
; отделим целую часть 
        fprem                          
; оставим от произведения лишь дробную часть,
        fsub    st(2), st              
        fxch    st(2)                  
; сохраним полученную цифру во временной ячейке
        fistp   word ptr [bp - 4]      
; и сразу выведем.
        mov     ah, 02h
        mov     dl, [bp - 4]
        add     dl, 30h
        int     21h
; Теперь, если остаток дробной части ненулевой
        fxch    st(1)                  
        ftst
        fstsw   ax
        sahf
; и мы вывели менее четырех цифр, продолжим.
        loopnz  @of4                   
;  число выведено. Осталось убрать мусор из стэка.
@of5:   fstp    st(0)                  
        fstp    st(0)                  
; Точнее, стэков.
        
        pop     dx
        pop     cx
        pop     ax
        ret
outfloat endp
exit:
	mov ax, 4c00h ;
	int 21h ;
main endp

IUnknown
Цитата
Цикл ему кажется слишком длинным: на инструкции loop outer_loop выдает "Relative jump out of range by 22h bytes". И почему-то стал ругаться на невинную команду fstsw ax: "Illegal immediate".
Ты б за структурой следил, и вторую процедуру записывал бы после того, как первая закончится - было бы меньше неожиданностей. Насчет цикла - не подтверждается, код прекрасно собирается (помнят еще руки-то smile.gif Цикл рассчитан до байта, добавляем еще одну инструкцию - 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
Hey
Да, фрагмент с exit'ом я не туда вставил. Действительно работает, спасибо)). Вопрос: что означает строка NL db 0Dh, 0Ah, "$" ?
IUnknown
Это для перевода строки: NewLine, символ CR = 13, символ LF = 10, и завершение строки - знак доллара smile.gif
Hey
Решительно не хватает знаний, чтобы успешно завершить программу. Я ее расширил, чтобы n возрастало до тех пор, пока не будет достигнута требуемая точность (0.0001). Вместо вывода значения функции при каждом из n (20, 40, 60 ....) прога выводит окончательное значение, причем не один раз((. Вывод n также не работает. Прилагаю.



.286p
.model small
.stack 100h
.data
mes db 'Operands not comparable', 13, 10, '$'
a dw 2
b dw 5
x dq 2.0
n dw 0
d dw 20
eps dd 0.0001
h dq ?
y dq ?
z 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

Сalc_h macro
        mov cx, n
	add cx, d
	mov n, cx
	dec cx
	
	; вычисляем шаг
	finit
	fild b
	fisub a
	fidiv n
	fst h		; Сохраняем шаг
        endm

main proc

	mov ax, @data
	mov ds, ax
	           
outer_:
	Сalc_h 
	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	
        
        call outfloat
        call OutInt ;выводим актуальное n
        finit

; далее повторяем с начала, но сохраняем в z для последующего сравнения с y
         
        Сalc_h 
	iCalcF b	; 1 / ln b
	fstp z
	iCalcF a	; 1 / ln a
	fadd z	; Складываем
	fdiv x	; Делим сумму на 2
	fstp z	; Сохр. начальное приближение
	fld x	;  

lesser_loop:
	fadd h	; X <- X + h
	CalcF
	fadd z
	fstp z	; Сохраняем измененное значение интеграла
	loop lesser_loop
	
	fld z
	fmul h	

        call outfloat
        
        call OutInt ;выводим актуальное n
        
        fld z
        fsub y
        fabs
        fcomp eps
        fstsw ax
        sahf

jp m2
jz m1
jc exit
jmp outer_
 
m1: jmp outer_ 
         
m2:
MOV DX, offset mes
mov Ah,09
int 21h


exit:
	mov ax, 4c00h ;
	int 21h ;
main endp


OutInt proc near
    xor ax,ax
    mov ax,n
    xor cx, cx
    mov  bx, 10
l1:
    xor     dx,dx
    div     bx
    push    dx
    inc     cx
    test ax, ax
    jnz l1
    mov ah, 02h
l2:    
    pop dx 
    loop l2    

    mov AH,9
    mov dx, offset NL    
    int 21h   
 ret
    outint endp


	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

@of1:   fld1                           
        fld     st(1)                  
        fprem                         
        fsub    st(2), st              
        fxch    st(2)                 
 
        xor     cx, cx

@of2:   fidiv   word ptr [bp - 2]     
        fxch    st(1)                  
        fld     st(1)                  
        fprem                          
        fsub    st(2), st              
        fimul   word ptr [bp - 2]      
        fistp   word ptr [bp - 4]     
        inc     cx
        push    word ptr [bp - 4]
        fxch    st(1)                  
        ftst
        fstsw   ax
        sahf
        jnz     short @of2
; Теперь выведем её.
        mov     ah, 02h
@of3:   pop     dx
        add     dl, 30h
        int     21h
; И так, пока не выведем все цифры.
        loop    @of3                  
; Теперь за дробную часть, для начала 

;проверив её существование.
        fstp    st(0)                 
        fxch    st(1)                  
        ftst
        fstsw   ax
        sahf
        jz      short @of5
; Если она  ненулевая, выведем точку
        mov     ah, 02h
        mov     dl, '.'
        int     21h
; и не более четырех цифр дробной части.
        mov     cx, 4
; Помножим дробную часть на десять,
@of4:   fimul   word ptr [bp - 2]      
        fxch    st(1)                  
        fld     st(1)                  
; отделим целую часть 
        fprem                          
; оставим от произведения лишь дробную 

часть,
        fsub    st(2), st              
        fxch    st(2)                  
; сохраним полученную цифру во временной 

ячейке
        fistp   word ptr [bp - 4]      
; и сразу выведем.
        mov     ah, 02h
        mov     dl, [bp - 4]
        add     dl, 30h
        int     21h
; Теперь, если остаток дробной части 

ненулевой
        fxch    st(1)                  
        ftst
        fstsw   ax
        sahf
; и мы вывели менее четырех цифр, продолжим.
        loopnz  @of4                   
;  число выведено. Осталось убрать мусор из 

стэка.
@of5:   fstp    st(0)                  
        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
Hey, я опять не понял, зачем тебе 2 цикла, в каждом из которых ты вычисляешь значения интегралов? Давай запишем алгоритм который тебе нужен, в псевдокоде. Итак, чтобы определить, при каком N значение интеграла найдено с точностью Eps, тебе достаточно:

   n <- 0
y <- 0.0;
repeat
prev <- y;
n <- n + DeltaN;
y <- Integral(a, b, n);
until abs(y - z) < eps;
print "finally Int = ", y, " when n = ", n


Всё, никаких двойных циклов, все прекрасно делается одним. А теперь смотри, как это записывается на ассемблере (я заменил переменную z на prev):

main proc
	mov ax, @data
	mov ds, ax
	
outer_:
	fld y
	fstp prev
	; fldz
	; fstp y

	; вычисляем шаг для текущего N
	Сalc_h
	
	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	; F(X)
	fadd y
	fstp y	; Сохраняем измененное значение интеграла
	loop inner_loop
	
	fld y
	fmul h	; нашли окончательное значение при текущем N
	fst y		; сохранили его назад в Y для последующего сравнения с prev
	
	; дублируем значение с вершины стека
	; fld st
	
	; выводим значение интеграла и N
	call outfloat
	call OutInt ; выводим актуальное n
	
	fld y
	fsub prev
	fabs
	fcomp eps
	fstsw ax
	sahf
	jp uncomp
	jz m1
	jc exit
m1:
	jmp outer_
uncomp:
	mov dx, offset mes
	mov ah, 09
	int 21h

exit:
	mov ax, 4c00h ;
	int 21h ;
main endp
Проще, правда, чем делать дважды одно и то же?

Теперь насчет
Цитата
Вывод n также не работает.
Процедуру OutInt проверять не пробовал? smile.gif

Вообще-то, она должна выглядеть так (вообще-то, жестко задавать в программе, что процедура выводит именно N - это бред, лучше перед ее вызовом занести ax <- N):

outint  proc    near
        push    cx
        push    dx
        push    bx
        push    ax
	
	mov ax, n  ; <--- !!! А этого лучше не делать !!!

; Проверяем число на знак.
        test    ax, ax
        jns     short @oi1
; Если оно отрицательное, выведем минус и оставим его модуль.
        mov     ah, 02h
        mov     dl, '-'
        int     21h
        pop     ax
        push    ax
        neg     ax
; Количество цифр будем держать в CX.
@oi1:   xor     cx, cx
        mov     bx, 10
@oi2:   xor     dx, dx
        div     bx
; Делим число на десять. В остатке получается последняя цифра.
; Сразу выводить её нельзя, поэтому сохраним её в стэке.
        push    dx
        inc     cx
; А с частным повторяем то же самое, отделяя от него очередную
; цифру справа, пока не останется ноль, что значит, что дальше
; слева только нули.
        test    ax, ax
        jnz     short @oi2
; Теперь приступим к выводу.
        mov     ah, 02h
@oi3:   pop     dx
; Извлекаем очередную цифру, переводим её в символ и выводим.
        add     dl, 30h
        int     21h
; Повторим ровно столько раз, сколько цифр насчитали.
        loop    @oi3
	
	; переводим строку после выведенного числа
	mov ah, 9
	mov dx, offset NL
	int 21h

        pop     ax
        pop     bx
        pop     dx
        pop     cx
        ret
outint  endp
С этими изменениями программа прекрасно отрабатывает:
F:\Asm30>trapece
2.5912
20
2.5898
40
2.5896
60
2.5895
80

F:\Asm30>

Hey
IUnknown, ты прав: уперся я в свое решение как лунатик и увидеть больше ничего не мог). С одним циклом все внятно. Спасибо за терпениеsmile.gif. Еще вопрос: в какой программе ты набираешь листинг? Явно не блокнот.
IUnknown
Цитата(Hey @ 15.09.2012 17:36) *
Еще вопрос: в какой программе ты набираешь листинг? Явно не блокнот.
Естественно. Подсветка синтаксиса же нужна smile.gif Я использую SciTE, на движке Scintilla.
Hey
Отлично, спасибо))
Hey
IUnknown, опять вынужден обратиться. Та же многострадальная программа, но для другой функции: требуется вычислить интеграл от функции sin (3x+3) / 7x+6. Беда в том, что я привязан к средствам процессора 286 и команду fsin использовать не могу. Иду окольным путем, через процедуру вычисления частичного тангенса fptan. Пока возникли две проблемы:
1. Решительно не хочет правильно разделить pi на 4. Это переменная four в формате слова. Эксперименты с присвоением ей другого формата (dq) или обозначения (например, 4.0. вместо 4) дают еще более неправильный результат.
2. После завершения энтой процедуры вычисления синуса возврат идет не к месту ее вызова, а к началу кода.

Что за напасть?

.286p
.model small
.stack 100h
.data
mes db 'Operands not comparable', 13, 10, '$'
a dw 1
b dw 5
x dd 1
n dw 0
d dw 20
eps dd 0.0001
two dw 2
three dw 3
seven dw 7
six dw 6
h dq ?
y dq ?
z dq ?
NL db 0Dh, 0Ah, "$"
STATUS  DW      ? 
FOUR    dw      4
C3      EQU     40H 
C2      EQU     04H 
C1      EQU     02H 
C0      EQU     01H 
 

.code
	
CalcF macro; На вершине стека д.б. "x"

fimul three 
fiadd three
call sin 

fstp y
fld x
fimul seven
fiadd six
fld y
fdivr

endm

iCalcF macro param

fild param
fimul three
fiadd three

call sin 
fstp y
fild param
fimul seven
fiadd six
fld y
fdivr

endm

Сalc_h macro
        mov cx, n
	add cx, d
	mov n, cx
	dec cx
	
	; вычисляем шаг
	finit
	fild b
	fisub a
	fidiv n
	fst h		; Сохраняем шаг
        endm

main proc

	mov ax, @data
	mov ds, ax
	           
outer_:
        fld y
        fstp z 
        Сalc_h 
	iCalcF b	
	fstp y
	iCalcF a    
        fadd y	; Складываем
	fidiv two ; Делим сумму на 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	
        fst y
        
        call outfloat
        call OutInt ;выводим актуальное n
        finit
		      
        fld y
        fsub z
        fabs
        fcomp eps
        fstsw ax
        sahf

jp m2
jz m1
jc exit
jmp outer_
 
m1: jmp outer_ 
         
m2:
MOV DX, offset mes
mov Ah,09
int 21h

exit:
	mov ax, 4c00h ;
	int 21h ;
main endp

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

@of1:   fld1                           
        fld     st(1)                  
        fprem                         
        fsub    st(2), st              
        fxch    st(2)                 
 
        xor     cx, cx

@of2:   fidiv   word ptr [bp - 2]     
        fxch    st(1)                  
        fld     st(1)                  
        fprem                          
        fsub    st(2), st              
        fimul   word ptr [bp - 2]      
        fistp   word ptr [bp - 4]     
        inc     cx
        push    word ptr [bp - 4]
        fxch    st(1)                  
        ftst
        fstsw   ax
        sahf
        jnz     short @of2
; Теперь выведем её.
        mov     ah, 02h
@of3:   pop     dx
        add     dl, 30h
        int     21h
; И так, пока не выведем все цифры.
        loop    @of3                  
; Теперь за дробную часть, для начала проверив её существование.
        fstp    st(0)                 
        fxch    st(1)                  
        ftst
        fstsw   ax
        sahf
        jz      short @of5
; Если она  ненулевая, выведем точку
        mov     ah, 02h
        mov     dl, '.'
        int     21h
; и не более четырех цифр дробной части.
        mov     cx, 4
; Помножим дробную часть на десять,
@of4:   fimul   word ptr [bp - 2]      
        fxch    st(1)                  
        fld     st(1)                  
; отделим целую часть 
        fprem                          
; оставим от произведения лишь дробную 

часть,
        fsub    st(2), st              
        fxch    st(2)                  
; сохраним полученную цифру во временной ячейке
        fistp   word ptr [bp - 4]      
; и сразу выведем.
        mov     ah, 02h
        mov     dl, [bp - 4]
        add     dl, 30h
        int     21h
; Теперь, если остаток дробной части ненулевой
        fxch    st(1)                  
        ftst
        fstsw   ax
        sahf
; и мы вывели менее четырех цифр, продолжим.
        loopnz  @of4                   
;  Осталось убрать мусор из стэка.
@of5:   fstp    st(0)                  
        fstp    st(0)                  

        leave

        mov AH,9
        mov dx, offset NL
        int 21h

        pop     dx
        pop     cx
        pop     ax
        ret
outfloat endp

OutInt proc near
    push    cx
        push    dx
        push    bx
        push    ax
    
    mov ax,n
    xor cx, cx
    mov  bx, 10
l1:
    xor     dx,dx
    div     bx
    push    dx
    inc     cx
    test ax, ax
    jnz l1
    mov ah, 02h
l2:    
    pop dx 
    add dl, 30h
    int 21h
    loop l2    

    mov AH,9
    mov dx, offset NL    
    int 21h   

    pop     ax
    pop     bx
    pop     dx
    pop     cx
    ret
    outint endp

SIN     PROC    near
 PUSH    DS 
 SUB     AX, AX 
 PUSH    AX 
 MOV     AX, CS 
 MOV     DS, AX 
 MOV     ES, AX 

DO_AGAIN: 

 FLDPI             ; PI        ; X 
 FIDIV   FOUR    ; PI/4      ; X 
 FXCH              ; X         ; PI/4 
 FPREM             ; R         ; PI/4 
 FSTSW   STATUS 
 FWAIT 
 MOV     AH, BYTE PTR STATUS+1 
 TEST    AH, C1          ; Определяется, необходимо ли вычитание PI/4 
 JZ      DO_R            ; Если 0, то не нужно 
 FSUBRP  ST(1), ST(0)    ; A = PI/4-R    ; ? 
 JMP     SHORT DO_FPTAN 
 
 DO_R: 
 FXCH              ; PI/4            ; R 
 FCOMP                   ; R         ; ? 
 
DO_FPTAN: 
 
  FPTAN  ; OPP             ; ADJ   Где 

OPP/ADJ=Tan(A) 
 
;-----  Определение того, что нужно - синус или косинус 
 
 TEST    AH, C3 or C1 
 
 JPE     DO_SINE 
 
 FXCH              ; ADJ             ; OPP 
 DO_SINE:          ; D         ; N 
 
 
   ; Вычисление N/SQR(N**2 + D**2) 
FMUL    ST(0),ST(0)    ; D**2      ; N 
FXCH    ST(1)          ; N         ; D**2 
FLD     ST(0)          ; N         ; N         ; D**2 
 FMUL    ST(0),ST(0)           ; N**2       ; N             ; D**2 
 FADD    ST(0), ST(2)      ; N**2 + D**2   ; N          ; D**2 
 FSQRT                      ;  SQR(N2 + D2)  ; N            ; D**2 
 FDIVRP  ST(1)           ; SIN(X)        ; D**2 
 FXCH    ST(1)           ; D**2            ; SIN(X) 
  FCOMP                 ; SIN(X)        ; ? 
  Test ah, C0
  jz return_inst
  fchs
  return_inst: 
  RET 

SIN  ENDP 

end main

 





















Hey
Порядок, нашел ошибки. Из процедуры SIN надо было убрать все эти DS-ES и сразу переходить к вычислениям. Но и в теле программы пришлось кое-что подправить...)
furosemide 40 mg canadian pharma
cialis daily
hydroxychloroquine online purcha
Amoxicillin And Amox Tr
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.