Вычисления с плавающей точкой
Выбери формат для чтения
Загружаем конспект в формате docx
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Глава 3. Вычисления с плавающей точкой
Все операции с плавающей точкой выполняет специальное устройство – сопроцессор – FPU (Floating Point Unit) (собственные регистры и команды).
В начале – отдельная микросхема.
Начиная с i486 – блок, встроенный в процессор.
§ 3.1. Типы данных FPU
Тип данных
Бит
Пределы
Короткое вещественное
32
1.1810-38 — 3.401038
Длинное вещественное
64
2.2310-308 — 1.7910308
Расширенное вещественное
80
3.3710-4932 — 1.18104932
0,625 = 0,101b = (нормализация в процессорах Intel) 1,01b × 2-1
мантисса = 1,01
порядок = -1
Первая цифра мантиссы всегда равна 1 ее можно не писать точность увеличивается на 1 бит.
Значение порядка хранят не в виде целого со знаком, а в виде суммы с некоторым числом так, чтобы хранить всегда только положительное число легко сравнивать вещественные числа.
Как хранится действительное число, разберем на примере короткого вещественного числа
s
e
f
31
30
23
22
порядок + смещение = характеристика
p + 127 = e
V = (-1)s × 2(e-127) × 1.f (0 < e < 255)
V = (-1)s × 2(-126) × 0.f (e = 0, f ≠ 0)
(денормализация, для работы с очень маленькими числами)
V = (-1)s × 0 (e = 0, f = 0)
V = (-1)s × ∞ (e = 255, f = 0)
V = NAN (e = 255, f ≠ 0)
(не-число)
A
C
1
1
1
1
31
30
23
22
V = (-1)1×2(78-127) × 1.0 = -2-49
короткое вещественное:
бит 31 знак мантиссы
биты 30 – 23 8-битная экспонента + 127
биты 22 – 0 23-битная мантисса без первой цифры
длинное вещественное:
бит 63 знак мантиссы
биты 62 – 52 11-битная экспонента + 1024
биты 51 – 0 52-битная мантисса без первой цифры
расширенное вещественное:
бит 79 знак мантиссы
биты 78 – 64 15-битная экспонента + 16 383
биты 63 – 0 64-битная мантисса с первой цифрой (то есть бит
63 равен 1).
FPU выполняет все вычисления в 80-битном расширенном формате, а 32- и 64-битные числа используются для обмена данными с основным процессором и памятью.
§ 3.2. Регистры FPU
Регистры данных – 8 основных регистров r0, r1, …, r7 (физические номера). Эти регистры рассматриваются как стек FPU (кольцо!). Вершина стека FPU – st(0), а более глубокие элементы – st(1), st(2), …, st(7) (логические номера).
r0
st(3)
r1
st(4)
r2
st(5)
r3
st(6)
r4
st(7)
r5
st(0) top (3-битовое поле) = 5
r6
st(1)
r7
st(2)
79
Если, регистр r5 называется st(0), то при записи в стек FPU числа, оно будет записано в регистр r4, который станет называться st(0), r5 станет называться st(1) и т.д.
К регистрам r0, r1, …, r7 нельзя обращаться напрямую (по именам).
Служебные регистры
Регистр состояния Status Word Register
swr
Регистр управления Control Word Register
cwr
Регистр тегов Tags Word Register
twr
15
Регистры указателей
адрес последней выполненной команды
ipr
адрес ее операнда
dpr
47
Регистр состояния swr – отражает текущее состояние FPU
15
14
13
12
11
10
09
08
07
06
05
04
03
02
01
00
b
c3
top
c2
c1
c0
es
sf
pe
ue
oe
ze
de
ie
c3, c2, c1, c0 - условные флаги (используются для условных переходов, аналог eflags)
top - номер регистра, являющегося вершиной стека FPU
es - общий флаг ошибки = 1, если произошло хотя бы одно из 6 исключений
sf - ошибка работы стека FPU (попытка писать в непустую позицию в стеке FPU или считать число из пустой позиции в стеке FPU)
6 флагов исключительных ситуаций:
pe - флаг неточного результата - результат не может быть
представлен точно
ue - флаг антипереполнения - результат слишком маленький
oe - флаг переполнения - результат слишком большой
ze - флаг деления на ноль - выполнено деление на ноль
de - флаг денормализованного операнда - выполнена операция над
денормализованным числом
ie - флаг недопустимой (недействительной) операции: - ∞ - ∞, ∞ + ∞
Регистр управления cwr – определяет особенности обработки численных данных
15
14
13
12
11
10
09
08
07
06
05
04
03
02
01
00
ic
rc
pc
pm
um
om
zm
dm
im
6 масок исключений:
pm - маска неточного результата
um - маска антипереполнения
om - маска переполнения
zm - маска деления на ноль
dm - маска денормализованного операнда
im - маска недопустимой операции
Если маскирующий бит установлен, исключения не происходит.
rc - управление округлением
m - значение в st, a < m < b
00
к ближайшему числу
01
к - ∞ (m = a)
10
к + ∞ (m = b)
11
к 0 (отбрасывается дробная часть)
pc - управление точностью
00
одинарная точность (32-битные числа)
10
двойная точность (64-битные числа)
11
расширенная точность (80-битные числа)
Регистр тегов twr содержит восемь пар бит, описывающих содержание каждого регистра данных
15
14
13
12
11
10
09
08
07
06
05
04
03
02
01
00
r7
r6
r5
r4
r3
r2
r1
r0
00
регистр содержит число
01
регистр содержит 0
10
регистр содержит нечисло, бесконечность, денормализованное число, NAN
11
регистр пуст
Регистры ipr и dpr содержат адрес последней выполненной команды (за некоторым исключением) и адрес ее операнда. Используются в обработчиках исключений для анализа вызвавшей его инструкции.
Принцип работы сопроцессора
§ 3.3. Инструкции (команды) FPU
Основные правила образования названий команд
1. Все названия инструкций начинаются с f – float
2. Вторая буква определяет тип операнда в памяти:
i – целое двоичное число
b – целое десятичное число
отсутствие буквы – вещественное число
3. Последняя буква p означает, что последним действием инструкции является извлечение (выталкивание) из стека FPU: st(0) помечается как пустой, top = top + 1.
4. Последняя или предпоследняя буква r (reversed) означает реверсионное следование операндов.
Инструкции пересылки данных
fld источник
Загрузить вещественное число в стек FPU.
top = top – 1
st(0)= источник
Источник:
- переменная
- st(i)
double x = -0.25, y = 1, z;
_asm{
finit ;инициализация сопроцессора
fld x ;st(0) = x
;...
fld st(0) ;копия вершины стека
}
fst приемник
Скопировать вещественное число из стека FPU.
приемник = st(0)
Приемник:
- переменная
- st(i)
fstp приемник
Считать вещественное число из стека FPU.
приемник = st(0)
st(0) помечается как пустой
top = top + 1
fild источник
Загрузить целое число в стек FPU. ...Преобразовывает целое число со знаком из источника (16-, 32- или 64-битная переменная) в вещественный формат.
fist приемник
Скопировать целое число из стека FPU. ...Попытка записи слишком большого числа, бесконечности или не-числа приводит к исключению «недопустимая операция».
fistp приемник
Считать целое число из стека FPU. ...Попытка записи слишком большого числа, бесконечности или не-числа приводит к исключению «недопустимая операция».
fxch [приемник]
Обменять местами два регистра стека FPU.
st(0) приемник (регистр стека FPU st(i))
st(0) st(1) (если приемник не указан)
fcmovxx приемник, источник (Pentium Pro, Pentium II и выше)
Условная пересылка данных. Это набор инструкций, которые копируют содержимое источника (st(i)) в приемник (st(0)), если удовлетворяется то или иное условие (условная пересылка данных).
Каждое условие соответствует тем или иным значениям флагов регистра flags. Подробнее – далее...
Арифметические инструкции
fadd [[приемник,] источник ]
Сложение вещественных чисел.
приемник = приемник + источник
Приемник
Источник
не указан
переменная
st(0)= st(0)+ переменная
st(0)
st(i)
st(0)= st(0)+ st(i)
st(i)
st(0)
st(i)= st(i)+ st(0)
не указан
не указан
st(1)= st(1)+ st(0)
st(0) помечается как пустой
top = top + 1
faddp приемник, источник
Сложение с выталкиванием из стека FPU.
Приемник
Источник
st(i)
st(0)
st(i)= st(i)+ st(0)
st(0) помечается как пустой
top = top + 1
fiadd источник
Сложение целых чисел. Источник: переменная, содержащая целое число.
st(0) = st(0) + переменная
int i = 10;
double y = 10;
_asm{
finit
fld y
fadd y
;st(0)==20
int i = 10;
double y = 10;
_asm{
finit
fld i
fadd i
;в st(0)«мусор»
int i = 10;
double y = 10;
_asm{
finit
fild i
fiadd i
;st(0)==20
fsub [[приемник,] источник]
Вычитание вещественных чисел.
приемник = приемник – источник
Приемник
Источник
не указан
переменная
st(0)= st(0)- переменная
st(0)
st(i)
st(0)= st(0)- st(i)
st(i)
st(0)
st(i)= st(i)- st(0)
не указан
не указан
st(1)= st(1)- st(0)
st(0) помечается как пустой
top = top + 1
fsubp приемник, источник
Вычитание с выталкиванием из стека FPU.
Приемник
Источник
st(i)
st(0)
st(i)= st(i)- st(0)
st(0) помечается как пустой
top = top + 1
fisub источник
Вычитание целых чисел. Источник: переменная, содержащая целое число.
st(0) = st(0) – переменная
fsubr [[приемник,] источник]
Обратное вычитание вещественных чисел.
приемник = источник – приемник
Приемник
Источник
не указан
переменная
st(0)= переменная - st(0)
st(0)
st(i)
st(0)= st(i)- st(0)
st(i)
st(0)
st(i)= st(0)- st(i)
не указан
не указан
st(1)= st(0)- st(1)
st(0) помечается как пустой
top = top + 1
fsubrp приемник, источник
Обратное вычитание с выталкиванием из стека FPU.
Приемник
Источник
st(i)
st(0)
st(i)= st(0)- st(i)
st(0) помечается как пустой
top = top + 1
fisubr источник
Обратное вычитание целых чисел. Источник: переменная, содержащая целое число.
st(0) = переменная – st(0)
double a = 5, b = 2;
_asm{
finit
fld a
fld b
;st(1)==5 st(0)==2
fsub st(0),st(1)
;st(0)==-3
double a = 5, b = 2;
_asm{
finit
fld a
fld b
;st(1)==5 st(0)==2
fsubr st(0),st(1)
;st(0)==3
fmul [[приемник,] источник ]
Умножение вещественных чисел.
приемник = приемник × источник
Приемник
Источник
не указан
переменная
st(0)= st(0) × переменная
st(0)
st(i)
st(0)= st(0) × st(i)
st(i)
st(0)
st(i)= st(i) × st(0)
не указан
не указан
st(1)= st(1) × st(0)
st(0) помечается как пустой
top = top + 1
fmulp приемник, источник
Умножение с выталкиванием из стека FPU.
Приемник
Источник
st(i)
st(0)
st(i)= st(i) × st(0)
st(0) помечается как пустой
top = top + 1
fimul источник
Умножение целых чисел. Источник: переменная, содержащая целое число.
st(0) = st(0) × переменная
fdiv [[приемник,] источник]
Деление вещественных чисел.
приемник = приемник / источник
Приемник
Источник
не указан
переменная
st(0)= st(0) / переменная
st(0)
st(i)
st(0)= st(0) / st(i)
st(i)
st(0)
st(i)= st(i) / st(0)
не указан
не указан
st(1)= st(1) / st(0)
st(0) помечается как пустой
top = top + 1
fdivp приемник, источник
Деление с выталкиванием из стека FPU.
Приемник
Источник
st(i)
st(0)
st(i)= st(i) / st(0)
st(0) помечается как пустой
top = top + 1
fidiv источник
Деление целых чисел. Источник: переменная, содержащая целое число.
st(0) = st(0) / переменная
fdivr [[приемник,] источник]
Обратное деление вещественных чисел.
приемник = источник / приемник
Приемник
Источник
не указан
переменная
st(0)= переменная / st(0)
st(0)
st(i)
st(0)= st(i)/ st(0)
st(i)
st(0)
st(i)= st(0)/ st(i)
не указан
не указан
st(1)= st(0)/ st(1)
st(0) помечается как пустой
top = top + 1
fdivrp приемник, источник
Обратное деление с выталкиванием из стека FPU.
Приемник
Источник
st(i)
st(0)
st(i)= st(0)/ st(i)
st(0) помечается как пустой
top = top + 1
fidivr источник
Обратное деление целых чисел. Источник: переменная, содержащая целое число.
st(0) = переменная / st(0)
fabs
Абсолютное значение. st(0)=|st(0)|
fchs
Изменить знак. st(0) = – st(0)
frndint
Округлить до целого. st(0) = [st(0)] (или ]st(0)[)
в rc режим округления
fsqrt
Извлечь квадратный корень. st(0) = √ st(0)
и другие инструкции...
double x = -0.25, y = 1, z;
_asm{
finit ;инициализация сопроцессора
fld x ;st(0) = x
fabs ;st(0) = |x|
fsqrt ;st(0) = sqrt(|x|)
fsub y ;st(0) = sqrt(|x|) - y
fst st(1) ;или fld st(0): st(1) == st(0)
fmul ;st(0) = (sqrt(|x|) - y)^2
fst z ;z = st(0)
}
При вычислениях с плавающей точкой следите, чтобы не происходило заполнение всех 8 регистров данных. В случае переполнения стека FPU регистров данных результат вычислений не определен. Следует, где это возможно, использовать команды с выталкиванием из стека FPU (заканчивающиеся символом p).
int i = 10; double z = 0;
_asm{
finit ;инициализация сопроцессора
mov ecx, i ;ecx = i (счетчик для команды loop)
l1:
fld1 ;st(0) = 1
fadd z ;st(0) = st(0) + z
fstp z ;z = st(0) и вытолкнуть
;при fst z результат после 10 итераций не определен!
loop l1
}
//y = 1/1! + 1/2! + ... + 1/i!
int i = 10;
double y;
_asm{
finit
;инициализация сопроцессора
fld1
;st(0) = 1
fld1
;st(0) = 1
;st(1) == 1 st(0) == 1
fst y
;y = st(0)
;теперь y == 1 == 1!/1 (первое слагаемое)
;st(1) == 1! st(0) == 1
mov ecx, i
;ecx = i
dec ecx
;ecx = ecx - 1 (счетчик для команды loop)
l1:
;сейчас st(1) == (i-1)! st(0) == i-1
fld1
;st(0) = 1
;st(2)==(i-1)! st(1) == i-1 st(0) == 1
faddp st(1), st(0)
;st(1) = st(1)+st(0) и вытолкнуть
;st(1)==(i-1)! st(0) == i
fmul st(1), st(0)
;st(1) = st(1)st(0)
;st(1) == i! st(0) == i
fld1
;st(0) = 1
;st(2) == i! st(1) == i st(0) == 1
fdiv st(0), st(2)
;st(0) = st(0)/st(2)
;st(2) == i! st(1) == i st(0) == 1/i!
fadd y
;st(0) = st(0) + y
;st(2) == i! st(1) == i st(0) == y + 1/i!
fstp y
;y = st(0) и вытолкнуть
;y == 1/1! + 1/2! + ... + 1/i!
;[st(1) == i! st(0) == i]
loop l1
}
Инструкции сравнения
fcom [источник ]
Сравнить вещественные числа
fcomp [источник]
Сравнить и вытолкнуть из стека FPU
fcompp [источник]
Сравнить и вытолкнуть из стека FPU два числа
Источник
переменная
st(0) <> переменная
st(i)
st(0) <> st(i)
не указан
st(0) <> st(1)
Инструкции устанавливают флаги c0, c2 и c3
Условие
c3 zf
c2 pf
c0 cf
st(0) > источник
st(0) < источник
1
st(0) = источник
1
не сравнимы
1
1
1
Если один из операндов не-число, происходит исключение «недопустимая операция», а если оно замаскировано (флаг im == 1 регистра управления cwr), все три флага устанавливаются в 1.
fcom ;или другие команды сравнения
fstsw ax ;ax = swr
sahf ;flags = ah: zf = c3, pf = c2, cf = c0
Теперь можно использовать условные команды (например jxx) как после команды cmp.
xx
n – not – не b – before – меньше
e – равно u – несравнимы (флаг pf == 1)
int key = 0;
double x = -0.25, y = 0, z;
_asm{
finit ;инициализация сопроцессора
fldz ;st(0) = 0
fcom y ;st(0) <> y
fstsw ax ;ax = sr
sahf ;flags = ah: zf = c3, pf = c2, cf = c0
je error
fld y
fld x
fdiv st(0), st(1) ;st(0) = st(0)/st(1)
fst z ;z = x / y
jmp fin
error:
mov key, 1
fin:
}
if (!key) printf("%lf\n", z);
else printf("Division by 0\n");
fucom [источник]
Сравнить вещественные числа без учета порядков
fucomp [источник]
Сравнить без учета порядков и вытолкнуть из стека FPU
fucompp [источник]
Сравнить … и вытолкнуть из стека FPU два числа
Источник
st(i)
st(0) <> st(i)
не указан
st(0) <> st(1)
ficom источник
Сравнить целые числа
ficomp источник
Сравнить целые и вытолкнуть из стека FPU. Источник: переменная, содержащая целое число. st(0) <> переменная
fcomi приемник, источник (Pentium Pro, Pentium II и выше) Сравнить вещественные числа и установить eflags.
fcomip приемник, источник
Сравнить, установить eflags и вытолкнуть из стека FPU.
Приемник Источник
st(0) st(i) st(0) <> st(i)
Не изменяют содержимого регистра ax и выполняются быстрее
fldz ;st(0) = 0
fcom y ;0 <> y
fstsw ax ;ax = sr
sahf ;flags = ah
je error
fldz ;st(0) = 0
fld y;st(0) = y, st(1) = 0
fcomi st(0), st(1);0 <> y
je error
ftst
Проверить, не содержит ли st(0) ноль. Инструкция устанавливает флаги c0, c2 и c3
fld y ;st(0) = y
ftst ;y <> 0
fstsw ax ;ax = sr
sahf ;flags = ah
je error
и другие инструкции...
§ 3.4. Примеры
Ввод / вывод
Ввод/вывод вещественного числа практически не отличается от ввода/вывода, рассмотренного ранее.
double x = 0;
char s[] = "%lf", s1[] = "%lf\n";
_asm{
finit ;инициализация сопроцессора
;=====Ввод=====
lea eax, x
;поместить адрес переменной x в eax
push eax
;поместить eax (адрес x) в сегмент стека,
;автоматически уменьшится указатель сегмента
;стека esp на 4 байта
lea eax, s
;поместить адрес строки s в eax
push eax
;поместить eax (адрес s) в сегмент стека,
;автоматически уменьшится указатель сегмента
;стека esp еще на 4 байта
call scanf_s
;вызвать функцию scanf_s(s,&x)
add esp, 8
;вернуть указатель сегмента стека esp в
;исходное состояние (увеличить на 8)
;=====Вывод=====надо бы push x, но нельзя
fld x
;st(0) = x
sub esp, 8
;указатель сегмента стека esp уменьшить на 8
fstp qword ptr [esp]
;st(0) (то есть x) загрузить в сегмент стека
;по адресу, который указан в регистре esp,
;и вытолкнуть
lea eax, s1
;поместить адрес строки s1 в eax
push eax
;поместить eax (адрес s1) в сегмент стека,
;автоматически уменьшится указатель сегмента
;стека esp еще на 4 байта
call printf
;вызвать функцию printf(s1,x)
add esp,12
;вернуть указатель сегмента стека esp в
;исходное состояние (увеличить на 12)
}
Вычисление значений функции на заданном отрезке
Необходимо вывести на консоль таблицу значений функции
на отрезке [-10; 10,4] с шагом 0,5 при значениях параметров a = 0,5; b=0; c=1.
Реализацию программы разобьем на несколько этапов.
Этап I
double a=0.5, b=0, c=1, x1=-10, x2=10.4, t=0.5;
double x, y;
int key;
char m1[]="-----------------------------\n";
char m2[]="| x | y |\n";
char m3[]="-----------------------------\n";
char s0[]="| %11.3lf | %11.3lf |\n";
char s1[]="| %11.3lf | ----------- |\n";
_asm{
;=== Заголовок таблицы ===
...
;=== Таблица значений функции y ===
...
}
Этап II
_asm{
;=== Заголовок таблицы ===
lea ebx,m1
push ebx
call printf
add esp,4
lea ebx,m2
push ebx
call printf
add esp,4
lea ebx,m3
push ebx
call printf
add esp,4
;=== Таблица значений функции y === }
Этап III
;=== Таблица значений функции y ===
finit
;инициализация сопроцессора
fld x1
;st(0) = x1
fstp x
;x = st(0) и вытолкнуть (x == x1)
;стек FPU пуст
fldz
;st(0) = 0
;st(0) == 0
begin:
fld x2
;st(0) = x2
;st(1) == 0 st(0) == x2
fsub x
;st(0) = st(0)-x
;st(1) == 0 st(0) == (x2–x)
fcomip st(0),st(1)
;st(0) сравнить с st(1) и вытолкнуть
;st(0) == 0
jb end
;если (x2-x)<0 (выход за правую границу),
;то переход на метку end
;===== Вычисление y(x) =====
. . .
;===== Вывод строки таблицы =====
. . .
fld x
;st(0) = x
;st(1) == 0 st(0) == x
fadd t
;st(0) = st(0)+t
;st(1) == 0 st(0) == (x+t)
fstp x
;x = st(0) и вытолкнуть
;x увеличился на шаг t
;st(0) == 0
jmp begin
;переход на метку begin
end:
Этап IV
;===== Вычисление y(x) =====
;если x<0, b!=0, то y=a*x*x+b
;если x>0, b==0, то y=(x-a)/(x-c)
; но если (x-c)==0 – деление на 0
;иначе y=x/c
; но если c==0 - деление на 0
;Стратегия: если значение функции в точке x
;определено, то результат помещаем в y
;и key присваиваем 0, иначе key присваиваем 1.
;В стеке FPU st(0) == 0
;случай x<0, b!=0, y=a*x*x+b
fld x
;st(0) = x
;st(1) == 0 st(0) == x
fcomip st(0),st(1)
;st(0) сравнить с st(1) и вытолкнуть
;st(0) == 0
jnb l2
;если x >= 0 (x не меньше 0),
;то переход на метку l2
fld b
;st(0) = b
;st(1) == 0 st(0) == b
fcomip st(0),st(1)
;st(0) сравнить с st(1) и вытолкнуть
;st(0) == 0
je l2
;если b == 0, то переход на метку l2
fld a
;st(0) = a
;st(1) == 0 st(0) == a
fmul x
;st(0) = st(0)*x
;st(1) == 0 st(0) == a*x
fmul x
;st(0) = st(0)*x
;st(1) == 0 st(0) == a*x*x
fadd b
;st(0) = st(0)+b
;st(1) == 0 st(0) == a*x*x+b
fstp y
;y = st(0) и вытолкнуть (y == a*x*x+b)
;st(0) == 0
mov key,0
;key = 0 (значение y определено)
jmp l1
;переход на метку l1
l2: ;случай x>0, b==0, y=(x-a)/(x-c)
fld x
;st(0) = x
;st(1) == 0 st(0) == x
fcomip st(0),st(1)
;st(0) сравнить с st(1) и вытолкнуть
;st(0) == 0
jbe l3
;если x <= 0 (x меньше или равно 0),
;то переход на метку l3
fld b
;st(0) = b
;st(1) == 0 st(0) == b
fcomip st(0),st(1)
;st(0) сравнить с st(1) и вытолкнуть
;st(0) == 0
jne l3
;если b != 0 (b не равно 0),
;то переход на метку l3
fld x
st(0) = x
;st(1) == 0 st(0) == x
fsub c
;st(0) = st(0)-c (st(0) == x-c)
fcomip st(0),st(1)
;st(0) сравнить с st(1) и вытолкнуть
;st(0) == 0
je l4
;если (x-c) == 0,
;то переход на метку l4
fld x
;st(0) = x
;st(1) == 0 st(0) == x
fsub a
;st(0) = st(0)-a
;st(1) == 0 st(0) == x-a
fld x
;st(0) = x
;st(2) == 0 st(1) == x-a st(0) == x
fsub c
;st(0) = st(0)-c
;st(2) == 0 st(1) == x-a st(0) == x-c
fdivp st(1),st(0)
;st(1) = st(1)/st(0) и вытолкнуть
;st(1) == 0 st(0) == (x-a)/(x-c)
fstp y
;y = st(0) и вытолкнуть (y == (x-a)/(x-c))
;st(0) == 0
mov key,0
;key = 0 (значение y определено)
jmp l1
;переход на метку l1
l3: ;случай y=x/c
fld c
;st(0) = c
;st(1) == 0 st(0) == c;
fcomip st(0),st(1)
;st(0) сравнить с st(1) и вытолкнуть
;st(0) == 0
je l4
;если c == 0,
;то переход на метку l4
fld x
;st(0) = x
;st(1) == 0 st(0) == x
fdiv c
st(0) = st(0)/c
;st(1) == 0 st(0) == x/c
fstp y
;y = st(0) и вытолкнуть (y == x/c)
;st(0) == 0
mov key,0
;key = 0 (значение y определено)
jmp l1
;переход на метку l1
l4: ;деление на 0
mov key,1
;key = 1 (значение y не определено)
l1:
Этап V
;===== Вывод строки таблицы =====
;Напомним, что если значение функции в точке x
;определено, то результат лежит в y и key == 0,
;иначе key == 1. В стеке FPU st(0) == 0
cmp key,0
;сравнить key c 0
je l5
;если равны, переход на метку l5
;~~~случай, когда y(x) не определен (key == 1)~~~
sub esp,8
;указатель сегмента стека esp уменьшить на 8
fld x
;st(0) = x st(1) == 0 st(0) == x
fstp qword ptr [esp]
;st(0) (то есть x) загрузить в сегмент стека
;в соответствии с указателем сегмента стека esp
;и вытолкнуть st(0) == 0
lea eax,s1
;поместить адрес строки s1 в eax
push eax
;поместить eax (адрес s1) в сегмент стека,
;автоматически уменьшится указатель сегмента
;стека esp еще на 4 байта
call printf
;вызвать функцию printf(s1,x)
add esp,12
;вернуть указатель сегмента стека esp в
;исходное состояние (увеличить на 12)
jmp l6 ;переход на метку l6
l5: ;~~~случай, когда y(x) определен (key == 0)~~~
sub esp,8
;указатель сегмента стека esp уменьшить на 8
fld y
;st(0) = y st(1) == 0 st(0) == y
fstp qword ptr [esp]
;st(0) (то есть y) загрузить в сегмент стека
;в соответствии с указателем сегмента стека esp
;и вытолкнуть st(0) == 0
sub esp,8
;указатель сегмента стека esp уменьшить на 8
fld x
;st(0) = x st(1) == 0 st(0) == x
fstp qword ptr [esp]
;st(0) (то есть x) загрузили в сегмент стека
;в соответствии с указателем сегмента стека esp
;и вытолкнуть st(0) == 0
lea eax,s0
;поместить адрес строки s0 в eax
push eax
;поместить eax (адрес s0) в сегмент стека,
;автоматически уменьшится указатель сегмента
;стека esp еще на 4 байта
call printf
;вызвать функцию printf(s0,x,y)
add esp,20
;вернуть указатель сегмента стека esp в
;исходное состояние (увеличть на 20)
l6: