Хочу разобраться с форматом IEEE 754, но не очень понимаю как оно работает - достаю из числа порядок и мантиссу и вот... что дальше с ними делать чтобы получить целое число до точки и после?
полагаю нужно мантиссу умножить на 10 в степени равной порядку. сколько "лишних" бит вылезло - это целая часть, остальное - дробная
KeSqueer не угадал - формат двоичный и степень соответственно 2 а не 10 Exp10der см. Код (Text): +-------------------------------------------------------------------------+ ¦ ¦ ¦ ¦ ¦ SOURCE ¦ ¦ ¦ ¦ +1 $title('Перевод числа с плавающей точкой в ASCII вид') ¦ ¦ ¦ ¦ ¦ ¦ name floating_to_ascii ¦ ¦ ¦ ¦ public floating_to_ascii ¦ ¦ extrn get_power_10:near, tos_status:near ¦ ¦ ; ¦ ¦ ; Эта подпрограмма переводит число с плавающей точкой ¦ ¦ ; из вершины стека FPU в строку типа ASCII и отделяет ¦ ¦ ; степень 10, масштабируя значение (в двоичном виде). ¦ ¦ ; Максимальная длина строки символов регулируется ¦ ¦ ; параметром, который должен быть больше 1. ¦ ¦ ; Не-нормальные значения, денормальные значения и псевдо ¦ ¦ ; -нули переводятся корректно. Однако, не-нормальные ¦ ¦ ; величины и псевдо-нули более не поддерживаются ¦ ¦ ; процессором i486 (в соответствии со стандартом IEEE) и ¦ ¦ ; внутренне не генерируются. Возвращаемое значение ¦ ¦ ; указывает сколько двоичных разрядов точности было ¦ ¦ ; потеряно в не-нормальных или денормальных значениях. ¦ ¦ ; Также указывается и величина мантиссы псевдо-нуля ¦ ¦ ; (двоичным порядком). Целые числа меньшие 10**18 ¦ ¦ ; переводятся точно, если принимающая строка ASCII ¦ ¦ ; символов содержит достаточное количество позиций для ¦ ¦ ; цифр этих чисел. В противном случае значение ¦ ¦ ; переводиться в научную нотацию. ¦ ¦ ; ¦ ¦ ; В зависимости от результата подпрограмма выдает ¦ ¦ ; следующие значения: ¦ ¦ ; ¦ ¦ ; 0 перевод выполнен, размер строки определен ¦ ¦ ; 1 недопустимые аргументы ¦ ¦ ; 2 точный целочисленный перевод, размер строки ¦ ¦ ; определен ¦ ¦ ; 3 неопределенность ¦ ¦ ; 4 + NaN (не-число) ¦ ¦ ; 5 - NaN ¦ ¦ ; 6 + Бесконечность ¦ ¦ ; 7 - Бесконечность ¦ ¦ ; 8 встречен псевдо-ноль, размер строки определен ¦ ¦ ; ¦ ¦ ; Интерфейс для вызова из PLM-386/486 ¦ ¦ ; ¦ ¦ ; floating_to_ascii: ¦ ¦ ; procedure (number, denormal_ptr, string_ptr, size_ptr, ¦ ¦ ; field_size, power_ptr) word external; ¦ ¦ ; declare (denormal_ptr, string_ptr, power_ptr, size_ptr) ¦ ¦ ; pointer; ¦ ¦ ; declare field_size word; ¦ ¦ ; string_size based size_ptr word; ¦ ¦ ; declare number real; ¦ ¦ ; declare denormal integer based denormal_ptr; ¦ ¦ ; ¦ ¦ ¦ ¦ ; declare power integer based power_ptr; ¦ ¦ ; end floating_to_ascii; ¦ ¦ ; ¦ ¦ ; Величина с плавающей точкой должна быть на вершине ¦ ¦ ; стека FPU. Эта подпрограмма требует три свободные ¦ ¦ ; регистра в стеке и после отработки выталкивает ¦ ¦ ; передаваемые значения из стека. Полученная строка ¦ ¦ ; будет иметь начальный символ либо "+", либо "-", ¦ ¦ ; указывая на знак величины. Затем следуют десятичные ¦ ¦ ; цифры в ASCII виде. Числовое значение строки типа ¦ ¦ ; ASCII будет равно (ASCII СТРОКА)*10**ПОРЯДОК. Если ¦ ¦ ; данное число было нулем, то строка будет содержать ¦ ¦ ; только знак и один символ 0. Величина размера строки ¦ ¦ ; (string_size) указывает на полную длину строки символов, ¦ ¦ ; включая символ знака. Строка (0) всегда будет ¦ ¦ ; содержать знак. Возможно, что размер строки будет ¦ ¦ ; меньше размера поля. Это бывает при нулях и целых ¦ ¦ ; числах. Псевдо-ноль выдает особый код возврата. При ¦ ¦ ; денормальных числах указывается степень двух ¦ ¦ ; представленного значения. Степень десяти и строка будут ¦ ¦ ; такими же, как если бы величина была бы простым нулем. ¦ ¦ ; ¦ ¦ ; Эта подпрограмма точно выдает десятичные целые до 18 ¦ ¦ ; цифр. Целые величины имеют десятичный показатель ¦ ¦ ; степени в строке из нулей. При нецелых величинах ¦ ¦ ; точность результата заключена в двух последних ¦ ¦ ; десятичных цифрах (двойная точность). Для ¦ ¦ ; масштабирования величины в диапазоне, приемлемом для ¦ ¦ ; данных типа BCD, используются команды возведения в ¦ ¦ ; степень. Для перевода используется режим округления, ¦ ¦ ; действующий при входе в подпрограмму. ¦ ¦ ; ¦ ¦ ; Следующие регистры не видны: ¦ ¦ ; ¦ ¦ ; eax ebx ecx edx esi edi eflags ¦ ¦ ; ¦ ¦ ; Определение стека. ¦ ¦ ; ¦ ¦ ebp_save equ dword ptr [ebp] ¦ ¦ es_save equ ebp_save + size ebp_save ¦ ¦ return_ptr equ es_save + size es_save ¦ ¦ power_ptr equ return_ptr + size return_ptr ¦ ¦ field_size equ power_ptr + size power_ptr ¦ ¦ size_ptr equ field_size + size size_ptr ¦ ¦ string_ptr equ size_ptr + size size_ptr ¦ ¦ denormal_ptr equ string_ptr + size string_ptr ¦ ¦ ¦ ¦ parms_size equ size power_ptr + size field_size + ¦ ¦ & size size_ptr + size string_ptr + ¦ ¦ & size denormal_ptr ¦ ¦ ; ¦ ¦ ; Определение используемых констант. ¦ ¦ ; ¦ ¦ BCD_DIGITS equ 18 ; Количество цифр в величине типа BCD ¦ ¦ WORD_SIZE equ 4 ¦ ¦ BCD_SIZE equ 10 ¦ ¦ MINUS equ 1 ; Определение выдаваемых значений ¦ ¦ NAN equ 4 ; Выбраные здесь точные величины - ¦ ¦ INFINITY equ 6 ; важны. Они должны соответствовать ¦ ¦ INDEFINITE equ 3 ; возможным выдаваемым значениям и ¦ ¦ PSEUDO_ZERO equ 8 ; тестироваться в том же порядке, как ¦ ¦ INVALID equ -2 ; показано в этой программе. ¦ ¦ ZERO equ -4 ¦ ¦ DENORMAL equ -6 ¦ ¦ UNNORMAL equ -8 ¦ ¦ NORMAL equ 0 ¦ ¦ EXACT equ 2 ¦ ¦ ; ¦ ¦ ; Определение положения временной области хранения. ¦ ¦ ; ¦ ¦ power_two equ word ptr [ebp - WORD_SIZE] ¦ ¦ bcd_value equ tbyte ptr power_two - BCD_SIZE ¦ ¦ bcd-byte equ byte ptr bcd_value ¦ ¦ fraction equ bcd_value ¦ ¦ ¦ ¦ lokal_size equ size power_two + size bcd_value ¦ ¦ ; ¦ ¦ ; Выделить достаточный объем стека для ¦ ¦ ; временных результатов. ¦ ¦ ; ¦ ¦ stack stackseg (lokal_size+6) ; Выделить пространство стека ¦ ¦ ; для локальных данных. ¦ ¦ +1 $eject ¦ ¦ ¦ ¦ code segment public er ¦ ¦ extrn power_table:qword ¦ ¦ ; ¦ ¦ ; Константы, используемые этой функцией. ¦ ¦ ; ¦ ¦ even ; Оптимизировать до 16 цифр. ¦ ¦ const10 dw 10 ; Подрегулировать значение для ¦ ¦ ; ; слишком больших BCD. ¦ ¦ ; ¦ ¦ ; Перевести биты C3, C2, C1 и C0 в ¦ ¦ ; значащие флаги и величины, используя ¦ ¦ ; процедуру tos_status. ¦ ¦ ; ¦ ¦ status_table db UNNORMAL, NAN, UNNORMAL+MINUS, ¦ ¦ & NAN+MINUS, NORMAL, INFINITY, ¦ ¦ & NORMAL+MINUS, INFINITY+MINUS, ¦ ¦ & ZERO, INVALID, ZERO+MINUS, INVALID, ¦ ¦ & DENORMAL, INVALID, DENORMAL+MINUS, INVALID ¦ ¦ floting_to_ascii proc ¦ ¦ ¦ ¦ call tos_status ; Посмотреть на состояние ST(0) ¦ ¦ ; ¦ ¦ ; Взять дескриптор из таблицы ¦ ¦ ; ¦ ¦ movzx eax, status_table[eax] ¦ ¦ cmp al,INVALID ; ST(0) пуст? ¦ ¦ jne not_empty ¦ ¦ ; ¦ ¦ ; ST(0) - пуст! Возвращает значение состояния. ¦ ¦ ; ¦ ¦ ret parms_size ¦ ¦ ; ¦ ¦ ; Удалить бесконечность из стека и выйти. ¦ ¦ ; ¦ ¦ found_infinity: ¦ ¦ fstp st(0) ; Оставить fstp ¦ ¦ jmp short exit_proc ¦ ¦ ; ¦ ¦ ; Длина строки слишком мала! ¦ ¦ ; Выдает код недопустимости. ¦ ¦ ; ¦ ¦ small_string: ¦ ¦ mov al,INVALID ¦ ¦ exit_proc: ¦ ¦ leave ; Восстановить стек ¦ ¦ ¦ ¦ pop es ¦ ¦ ret parms_size ¦ ¦ ; ¦ ¦ ; В ST(0) находится NaN или ¦ ¦ ; неопределенность. Сохранить значение ¦ ¦ ; в памяти и просмотреть дробное поле ¦ ¦ ; для того, чтобы отличить ¦ ¦ ; неопределенность от обычного NaN. ¦ ¦ ; ¦ ¦ NAN_or_indefinite: ¦ ¦ fstp fraction ; Для проверки - удалить значение ¦ ¦ ; из стека. ¦ ¦ test al,MINUS ; Посмотреть бит знака. ¦ ¦ fwait ; Убедиться в выполнении сохранения. ¦ ¦ jz exit_proc ; Если положительный знак, то не ¦ ¦ ; может быть неопределенностью. ¦ ¦ ¦ ¦ mov ebx,0C0000000H ; Подавить верхние 32 разряда дробной ¦ ¦ ; части. ¦ ¦ ¦ ¦ ; Сравнить разряды 63-32 ¦ ¦ sub ebx,dword ptr fraction + 4 ¦ ¦ ¦ ¦ ; Разряды 31-0 должны быть нулями ¦ ¦ or ebx,dword ptr fraction ¦ ¦ jnz exit_proc ¦ ¦ ¦ ¦ ; Установить выдаваемое значение на ¦ ¦ ; неопределенность ¦ ¦ mov al,INDEFINITE ¦ ¦ jmp exit_proc ¦ ¦ ; ¦ ¦ ; Выделить место в стеке для локальных переменных ¦ ¦ ; и установить параметр адресации. ¦ ¦ ; ¦ ¦ not_empty: ¦ ¦ push es ; Сохранить рабочий регистр ¦ ¦ enter local_size,0 ; Установить адресацию стека ¦ ¦ ¦ ¦ ; Проверить, достаточно ли места в строке ¦ ¦ mov ecx,field_size ¦ ¦ cmp ecx,2 ¦ ¦ jl small_string ¦ ¦ ¦ ¦ dec ecx ; Установить символ знака ¦ ¦ ¦ ¦ ; Посмотреть, может быть строка слишком большая для типа BCD ¦ ¦ cmp ecx,BCD_DIGITS ¦ ¦ jbe size_ok ¦ ¦ ¦ ¦ ; Иначе установить максимальный размер строки ¦ ¦ mov ecx,BCD_DIGITS ¦ ¦ size_ok: ¦ ¦ cmp al,INFINITY ; Может быть бесконечность? ¦ ¦ ¦ ¦ ; Возвратить значение состояния для + или - бесконечности ¦ ¦ jge found_infinity ¦ ¦ ¦ ¦ cmp al,NAN ; Можеть быть NaN или ¦ ¦ jge NAN_or_indefinite ; неопределенность ¦ ¦ ¦ ¦ ; ¦ ¦ ; Установить выдаваемое значение по умолчанию ¦ ¦ ; и проверить, нормализовано ли число. ¦ ¦ ; ¦ ¦ fabs ; Использовать только положительные значения ¦ ¦ ; ¦ ¦ ; Бит знака в регистре AL содержит истинный знак величины. ¦ ¦ ; ¦ ¦ xor edx,edx ; Подготовить константу 0 ¦ ¦ mov edi,denormal_ptr ; Обнулить счетчик денормальных чисел ¦ ¦ mov [edi],dx ¦ ¦ mov ebx,power_ptr ; Обнулить значение степени десяти ¦ ¦ mov [ebx],dx ¦ ¦ mov dl,al ¦ ¦ and dl,1 ¦ ¦ add dl,EXACT ¦ ¦ cmp al,ZERO ; Проверить на ноль ¦ ¦ jae convert_integer ; Перейти программу возведения в ¦ ¦ ; степень, если значение равно нулю. ¦ ¦ ¦ ¦ fstp fraction ¦ ¦ fwait ¦ ¦ mov al,bcd_byte + 7 ¦ ¦ or byte ptr bcd_byte +7,80h ¦ ¦ fld fraction ¦ ¦ fxtract ¦ ¦ test al,80h ¦ ¦ jnz normal_value ¦ ¦ ¦ ¦ fld1 ¦ ¦ fsub ¦ ¦ ftst ¦ ¦ fstsw ax ¦ ¦ sahf ¦ ¦ jnz set_unnormal_count ¦ ¦ ; ¦ ¦ ; Найден псевдо-ноль ¦ ¦ ; ¦ ¦ fldlg2 ; Оценить степень десяти ¦ ¦ add dl,PSEUDO_ZERO - EXACT ¦ ¦ fmulp st(2),st ¦ ¦ fxch ; Взять степеть десяти ¦ ¦ fistp word ptr [ebx] ; Установить степень десяти ¦ ¦ jmp convert_integer ¦ ¦ ¦ ¦ set_unnormal_count: ¦ ¦ fxtract ; Взять исходную дробь, ¦ ¦ ; и нормализовать. ¦ ¦ fxch ; Взять счетчик не-нормальных чисел ¦ ¦ fchs ¦ ¦ fistp word ptr [edi] ; Установить счетчик ¦ ¦ ; не-нормальных чисел ¦ ¦ ¦ ¦ ; ¦ ¦ ; Вычислить десятичную величину вместе с этим числом ¦ ¦ ; внутри одного порядка. ¦ ¦ ; ¦ ¦ ; Всегда при округлении будет присутствовать ошибка ¦ ¦ ; из-за потери точности. В результате, мы ¦ ¦ ; преднамеренно при вычислении порядка не стали ¦ ¦ ; рассматривать LOG10 от значения дроби. Так как ¦ ¦ ; дробь всегда больше или равна 1 и меньше двух, то ¦ ¦ ; LOG10 от этой дроби не изменяет основной точности ¦ ¦ ; функции. Для того, чтобы получить десятичный ¦ ¦ ; порядок величины, надо просто умножить степень ¦ ¦ ; двух на LOG10(2) и округлить результат с ¦ ¦ ; отсечением до целого. ¦ ¦ ; ¦ ¦ normal_value: ¦ ¦ fstp fraction ; Для дальнейшего использования ¦ ¦ ; сохранить поле дроби. ¦ ¦ fist power_twq ; Сохранить степень двух ¦ ¦ fldlg2 ; Взять LOG10(2) ¦ ¦ ; Теперь можно безопасно использовать ¦ ¦ ; power_two ¦ ¦ fmul ; Подготовить LOG10 от порядка числа ¦ ¦ fistp word ptr [ebx] ; Здесь может быть применен любой ¦ ¦ ; режим округления ¦ ¦ ; ¦ ¦ ; Проверить, чтобы величина числа ¦ ¦ ; интерпретировалась как целое. ¦ ¦ ; ¦ ¦ ; CX имеет максимально позволенное количество десятичных цифр. ¦ ¦ ; ¦ ¦ fwait ; Ожидать допустимой степени десяти ¦ ¦ ; ¦ ¦ ; Возвести десять в степень величины значения ¦ ¦ ; ¦ ¦ movsx si,word ptr [ebx] ¦ ¦ sub esi,ecx ; Подготовить в AX необходимый ¦ ¦ ; коэффициент масштабирования. ¦ ¦ ja adjust_result ; Перейти, если число не подходит ¦ ¦ ; ¦ ¦ ; Число между 1 и 10**(field_size - размер поля) ¦ ¦ ; Проверить на целочисленность ¦ ¦ ; ¦ ¦ fild power_two ; Восстановить начальное значение ¦ ¦ sub dl,NORMAL-EXACT ; Перевести в точное выдаваемое ¦ ¦ ; значение ¦ ¦ fld farction ¦ ¦ fscale ; Подготовить полную величину, в этой ¦ ¦ ; команде она не портится ¦ ¦ fst st(1) ; Скопировать значение для сравнения ¦ ¦ frndint ; Проверить на целочисленность ¦ ¦ fcomp ; Сравнить значения ¦ ¦ fstsw ax ; Сохранить состояние ¦ ¦ sahf ; C3 = 1, значит это была целое число ¦ ¦ ¦ ¦ jnz convert_integer ¦ ¦ ¦ ¦ fstp st(0) ; Удалить нецелочисленное значение ¦ ¦ add dl,NORMAL-EXACT ; Восстановить начальнное выдаваемое ¦ ¦ ; значение ¦ ¦ ; ¦ ¦ ; Масштабировать число внутри диапазона, ¦ ¦ ; позволенного форматом BCD. Операция ¦ ¦ ; масштабирования выдает число внутри одного ¦ ¦ ; десятичного порядка величины наибольшего ¦ ¦ ; десятичного числа, представимого для данной длины ¦ ¦ ; строки. ¦ ¦ ; ¦ ¦ ; Величина степени десяти для масштабирования ¦ ¦ ; находится в регистре SI. ¦ ¦ ; ¦ ¦ adjust_result: ¦ ¦ mov eax,esi ; Подготовить для возведения в ¦ ¦ ; степень ¦ ¦ mov word ptr [ebx],ax ; Установить начальную величину ¦ ¦ ; степени десяти ¦ ¦ neg eax ; Вычесть единицу для каждого порядка ¦ ¦ ; величины, на который масштабируется ¦ ¦ ; значение. ¦ ¦ call get_power_10 ; Коэффициент масштабирования ¦ ¦ ; представляется как порядок и дробь. ¦ ¦ fld fraction ; Взять дробь ¦ ¦ fmul ; Комбинировать дроби ¦ ¦ mov esi,ecx ; Возвести десять в максимальную ¦ ¦ ; степень ¦ ¦ shl esi,3 ; Для того, чтобы значение BCD ¦ ¦ ; входило в строку ¦ ¦ fild power_two ; Комбинировать степень двух ¦ ¦ faddp st(2),st ¦ ¦ fscale ; Подготовить полное значение, ¦ ¦ ; порядок остался не тронутым ¦ ¦ fstp st(1) ; Удалить порядок ¦ ¦ ; ¦ ¦ ; Проверьте установленное значение по таблице ¦ ¦ ; точных степеней десяти. Суммарные ошибки оценки ¦ ¦ ; величины и степенной функции могут привести к ¦ ¦ ; тому, что значение одного порядка величины будет ¦ ¦ ; либо слишком маленькое, либо слишком большое для ¦ ¦ ; поля типа BCD. Для устранения этой проблемы ¦ ¦ ; протестируйте полученное значение - является ли ¦ ¦ ; оно слишком большим или слишком маленьким. Затем ¦ ¦ ; отрегулируйте его и значение степени десяти. ¦ ¦ ; ¦ ¦ test_power: ¦ ¦ ; ¦ ¦ ; Сравните с точной степенью. Используйте следующую ¦ ¦ ; степень при уменьшении CX на единицу. ¦ ¦ ; ¦ ¦ fcom power_table[esi]+type power_table ¦ ¦ fstsw ax ; Не надо ждать ¦ ¦ sahf ; Если C3=C0=0, то слишком большое ¦ ¦ jb test_for-small ¦ ¦ fidiv const10 ; Иначе установить значение ¦ ¦ and dl,not EXACT ; Удалить флаг того, что число точное ¦ ¦ inc word ptr [ebx] ; Установить значение степени десяти ¦ ¦ jmp short in_range ; Перевести значение в целое типа BCD ¦ ¦ ¦ ¦ test_for_small: ¦ ¦ fcom power_table[esi] ; Проверить относительный размер ¦ ¦ ¦ ¦ fstsw ax ; Не ждать ¦ ¦ sahf ; Если C0 = 0, то ST(0) больше или ¦ ¦ ; равен нижшей границе ¦ ¦ jc in_range ; Перевести значение в целое типа BCD ¦ ¦ fimul const10 ; Подогнать значение под диапазон ¦ ¦ dec word ptr [ebx] ; Подобрать значение степени десяти ¦ ¦ in_range: ¦ ¦ frndint ; Подготовить целое значение ¦ ¦ ; ¦ ¦ ; Утверждение: 0 <= TOS <= 999,999,999,999,999,999 ¦ ¦ ; Число TOS будет точно представлено ¦ ¦ ; 18-ю цифрами в формате BCD. ¦ ¦ ; ¦ ¦ convert_integer: ¦ ¦ fbstp bcd_value ; Сохранить число в формате BCD ¦ ¦ ; ¦ ¦ ; При сохранении формата BCD установить регистры ¦ ¦ ; для перевода в ASCII вид. ¦ ¦ ; ¦ ¦ mov esi,BCD_SIZE-2 ; Инициализировать значение ¦ ¦ ; индекса BCD ¦ ¦ mov cx,0f04h ; Установить счетчик сдвига и маску ¦ ¦ mov ebx,1 ; Установить начальный размер ASCII ¦ ¦ ; поля для знака ¦ ¦ mov edi,string_ptr ; Взять адрес начала ASCII строки ¦ ¦ mov ax,ds ; Скопировать DS в ES ¦ ¦ mov es,ax ¦ ¦ cld ; Установить режим автоматического ¦ ¦ ; добавления единицы ¦ ¦ mov al,'+' ; Очистить поле знака ¦ ¦ test dl,MINUS ; Проверить на отрицательное значение ¦ ¦ jz positive_result ¦ ¦ ¦ ¦ mov al,'-' ¦ ¦ positive_result: ¦ ¦ stosb ; Установить указатель строки на ¦ ¦ ; последний знак ¦ ¦ and dl,not MINUS ; Выключить бит знака ¦ ¦ fwait ; Ожидать окончания команды fbstp ¦ ¦ ; ¦ ¦ ; Используемые регистры: ¦ ¦ ; ¦ ¦ ; AH: байт со значением типа BCD ¦ ¦ ; AL: значение строки ASCII ¦ ¦ ; DX: возвращаемое значение ¦ ¦ ; CH: маска BCD = 0fh ¦ ¦ ; CL: счетчик сдвига BCD = 4 ¦ ¦ ; BX: ширина поля строки ASCII ¦ ¦ ; ESI: индекс поля BCD ¦ ¦ ; DI: указатель поля строки BCD ¦ ¦ ; DS, ES: адрес сегмента строки ASCII ¦ ¦ ; ¦ ¦ ; Удалить начальные нули из числа. ¦ ¦ ; ¦ ¦ ; ¦ ¦ skip_leading_zeroes: ¦ ¦ mov ah,bcd_byte[esi] ; Взять байт BCD ¦ ¦ mov al,ah ; Скопировать значение ¦ ¦ shr al,cl ; Взять верхнюю по порядку цифру ¦ ¦ and al,0fh ; Установить флаг нуля ¦ ¦ jnz enter_odd ; Выйти из цикла, если в начале ¦ ¦ ; обнаружены не нули ¦ ¦ ¦ ¦ mov al,ah ; Взять снова байт BCD ¦ ¦ and al,0fh ; Взять нижнюю по порядку цифру ¦ ¦ jnz enter-even ; Выйти из цикла, если обнаружены ¦ ¦ ; ненулевые цифры ¦ ¦ ¦ ¦ dec esi ; Уменьшить индекс BCD ¦ ¦ jns skip_leading_zeroes ¦ ¦ ; ¦ ¦ ; Вся мантисса состоит из нулей ¦ ¦ ; ¦ ¦ mov al,'0' ; Установить начальный ноль ¦ ¦ stosb ¦ ¦ inc ebx ; Увеличить длину строки ¦ ¦ jmp short exit_with_value ¦ ¦ ; ¦ ¦ ; Теперь расширяем строку цифрами от ¦ ¦ ; 0 до 9 по одной цифре на байт ¦ ¦ ; ¦ ¦ digit_loop: ¦ ¦ mov ah,bcd_byte[esi] ; Взять байт BCD ¦ ¦ mov al,ah ¦ ¦ shr al,cl ; Взять верхнюю по порядку цифру ¦ ¦ enter_odd: ¦ ¦ add al,'0' ; Перевести в ASCII ¦ ¦ stosb ; Занести цифру в строку ASCII ¦ ¦ mov al,ah ; Взять нижнюю по порядку цифру ¦ ¦ and al,0fh ¦ ¦ inc ebx ; Увеличить счетчик размера поля ¦ ¦ enter-even: ¦ ¦ add al,'0' ; Перевести в ASCII ¦ ¦ stosb ; Занести цифру в строку ASCII ¦ ¦ inc ebx ; Увеличить счетчик размера поля ¦ ¦ dec esi ; Перейти к следующему байту BCD ¦ ¦ jns digit_loop ¦ ¦ ; ¦ ¦ ; Перевод закончен. Установить размер ¦ ¦ ; строки и остаток ¦ ¦ ; ¦ ¦ exit_with_value: ¦ ¦ mov edi,size_ptr ¦ ¦ mov word ptr [edi],bx ¦ ¦ mov eax,edx ; Установить выдаваемое значение ¦ ¦ jmp exit_proc ¦ ¦ ¦ ¦ floating_to_ascii endp ¦ ¦ code ends ¦ ¦ end ¦ ¦ ¦ ¦ ¦ ¦ +1 $title(Вычислить значение 10**AX) ¦ ¦ ¦ ¦ ; Эта подпрограмма вычисляет значение степени ¦ ¦ ; 10**EAX. Точный результат выдается для значений в ¦ ¦ ; диапазоне 0 <= EAX < 19. Все регистры прозрачны ¦ ¦ ; и значение выдается в TOS как два числа: порядок ¦ ¦ ; в ST(1) и дробь в ST(0). Величина порядка может ¦ ¦ ; превышать наибольший порядок числа в расширенном ¦ ¦ ; вещественном формате. В программе используются ¦ ¦ ; три стековых регистра. ¦ ¦ ; ¦ ¦ name get_power_10 ¦ ¦ public get_power_10, power_table ¦ ¦ ¦ ¦ stack stackseg 8 ¦ ¦ ¦ ¦ code segment public er ¦ ¦ ; ¦ ¦ ; Использовать точные значения от 1.0 до 1E18. ¦ ¦ ; ¦ ¦ even ; Оптимизировать 16-ти битовый доступ ¦ ¦ power_table dq 1.0,1e1,1e2,1e3 ¦ ¦ ¦ ¦ dq 1e4,1e5,1e6,1e7 ¦ ¦ ¦ ¦ dq 1e8,1e9,1e10,1e11 ¦ ¦ ¦ ¦ dq 1e12,1e13,1e14,1e15 ¦ ¦ ¦ ¦ dq 1e16,1e17,1e18 ¦ ¦ ¦ ¦ get_power_10 proc ¦ ¦ cmp eax,18 ; Проверить диапазон 0 <= AX < 19 ¦ ¦ ja out_of_range ¦ ¦ ¦ ¦ fld power_table[eax*8] ; Взять точное значение ¦ ¦ fxtract ; Отделить степень и ¦ ¦ ; дробную часть ¦ ¦ ret ; Оставить fxtract ¦ ¦ ; ¦ ¦ ; Вычислить значение, используя команду возведения ¦ ¦ ; в степень. Используются следующие соотношения: ¦ ¦ ; ¦ ¦ ; 10**X = 2**(log2(10)*X) ¦ ¦ ; 2**(I+F) = 2**I * 2**F ¦ ¦ ; ¦ ¦ ; Если ST(1) = I и ST(0) = 2**F, то команда fscale ¦ ¦ ; выдает 2**(I+F). ¦ ¦ ; ¦ ¦ out_of_range: ¦ ¦ ¦ ¦ fldl2t ; TOS = LOG2(10) ¦ ¦ enter 4,0 ¦ ¦ ¦ ¦ ; Сохранить значение степени десяти, P ¦ ¦ ; ¦ ¦ mov [ebp-4],eax ¦ ¦ ¦ ¦ ; TOS, X = LOG2(10)*P = LOG2(10**P) ¦ ¦ ; ¦ ¦ fimul dword ptr [ebp-4] ¦ ¦ fld1 ; Установить TOS = -1.0 ¦ ¦ fchs ¦ ¦ fld st(1) ; Скопировать значение степени по ¦ ¦ ; основанию два ¦ ¦ frndint ; TOS = I; -бесконечность < I <= X, ¦ ¦ ; где I - целое ¦ ¦ ; Режим округления не имеет значения ¦ ¦ fxch st(2) ; TOS = X, ST(1) = -1.0 ¦ ¦ ; ST(2) = I ¦ ¦ fsub st,st(2) ; TOS, F = X-I: ¦ ¦ ; -1.0 < TOS <= 1.0 ¦ ¦ ¦ ¦ ; Восстановить начальный режим управления точностью ¦ ¦ pop eax ¦ ¦ f2xm1 ; TOS = 2**(F) - 1.0 ¦ ¦ leave ; Восстановить стек ¦ ¦ fsubr ; Подготовить 2**(F) ¦ ¦ ret ; Оставить fsubr ¦ ¦ ¦ ¦ get_power_10 endp ¦ ¦ ¦ ¦ code ends ¦ ¦ end ¦ ¦ ¦ ¦ +1 $title(Определение содержимого регистра TOS) ¦ ¦ ; ¦ ¦ ; Эта подпрограмма выдает значение от 0 до 15 в ¦ ¦ ; регистр EAX в соответствии с содержанием вершины ¦ ¦ ; стека FPU. Все регистры прозрачны, поэтому ошибки ¦ ¦ ; исключены. Выдаваемое значение соответствует ¦ ¦ ; битам C3, C2, C1 и C0 команды FXAM. ¦ ¦ ; ¦ ¦ name tos_status ¦ ¦ public tos_status ¦ ¦ ¦ ¦ stack stackseg 6 ¦ ¦ ¦ ¦ code segment public er ¦ ¦ ¦ ¦ tos_status proc ¦ ¦ fxam ; Взять состояние регистра TOS ¦ ¦ fstsw ax ; Взять текущее состояние ¦ ¦ mov al,ah ; Положить биты 10-8 в биты 2-0 ¦ ¦ and eax,4007h ; Маскировать биты C3, C2, C1 и C0 ¦ ¦ shr ah,3 ; Положить бит C3 в бит 11 ¦ ¦ or al,ah ; Положить бит C3 в бит 3 ¦ ¦ mov ah,0 ; Очистить возвращаемое значение ¦ ¦ ret ¦ ¦ ¦ ¦ tos_status endp ¦ ¦ ¦ ¦ code ends ¦ ¦ end ¦ ¦ ¦ ¦ ¦ +-------------------------------------------------------------------------+ и аттач
edemko Эм.. вас же забанили за исчерпание лимита бесполезных сообщений, как это вы пишите не понимаю оО.
Код (Text): f2.0 dt 2.0 ... entry $ finit fld [f2.0] fld1 fdiv st0,st1 ; st0 = 1.0/2.0, st1 = 2.0 fdivr st0,st1 ; st0 = 2.0/0.5, st1 = 2.0 fdivp ; st0 = 2.0/4.0 fld1 fdivrp ; st0 = 1.0/0.5 push 10 fidiv dword[esp]; st0 = 2.0/10 add esp,4 ...
aggro fdivx поддерживает деление (не)нормализированных, деление на 0. На входе инициализирует fpu. На выходете st0 = результат работы. ps: переписка... для отбрасыввания дроби пользуем fisttp [mem16/mem32/mem64]; icq не пользуюсь из-за ограниченного доступа в интернет Успехов, я думал чекнусь. Код (Text): format pe gui 4.0 include 'win32ax.inc' section '' code import readable writable executable library advapi32, 'advapi32.dll',\ kernel32, 'kernel32.dll',\ user32, 'user32.dll' include 'api\advapi32.inc' include 'api\kernel32.inc' include 'api\user32.inc' ;include '../fl.inc' f1 dq 01000000'00000000'00000000'00000000''00000000'00000000'00000000'00000000b dw 0'000000000000000b f2 dq 01100000'00000000'00000000'00000000''00000000'00000000'00000000'00000000b dw 0'000000000000000b .f1 dt 1.0 .f2 dt 2.0 entry $ stdcall fdivx,.f1,.f2 invoke ExitProcess,0 proc fdivx; dividend:dword, divisor:dword pushfd push eax finit mov eax,[esp+16] call .test mov eax,[esp+12] call .test fxtract fxch st2 fxtract fdivp st3,st0 fsubp st1,st0 fxch fscale jmp .done .test: fld tbyte[eax] fxam fstsw ax and ah,01000101b cmp ah,00000100b je .test_ok cmp ah,01000100b je .test_ok mov dword[esp],.done fldz .test_ok: pop eax jmp eax .done: pop eax popfd ret 8 endp
*.exe в аттаче. Возможны ошибки(но вряд), я просто хочу спать ь Код (Text): format pe gui 4.0 include 'win32ax.inc' section '' code import readable writable executable library advapi32, 'advapi32.dll',\ kernel32, 'kernel32.dll',\ user32, 'user32.dll' include 'api\advapi32.inc' include 'api\kernel32.inc' include 'api\user32.inc' f1 dq 00000000'00000000'00000000'00000000''00000000'00000000'00000000'00000001b ;1*2^-16445 dw 0'000000000000000b ;... f2 dq 10000000'00000000'00000000'00000000''00000000'00000000'00000000'00000000b ;1*2^-16382 dw 0'000000000000001b ;... f3 dq 11111111'11111111'11111111'11111111''11111111'11111111'11111111'11111111b dw 1'111111111111111b entry $ ;finit fld tbyte[f2] fld tbyte[f1] call fdivx fscale ;fabs ;fisttp qword[f1] invoke ExitProcess,0 ; -> st0 = dividend, st1 = divisor ; <- st0 = significand, st1 = exponent proc fdivx push eax call fxtractx ;st0=a1,st1=a2,st2=B fxch st2 ;st0=B,st1=a2,st2=a1 call fxtractx ;st0=b1,st1=b2,st2=a2,st3=a1 fdivp st3,st0 ;st0=b2,s1=a2,st2=a1/b1 fsubp st1,st0 ;st0=a2-b2,st1=a1/b1 fxch ;st0 = мантисса, st1=експонента ftst ;куку? fstsw ax; 3 2 0 ;сохранить инф. о числе and ah,01000101b ;нам нужны только fpu.sw.{c0,c2,c3} cmp ah,01000101b jne @f ;претензий нет fstp st0 ;выкинуть плохой результат fldz ;загрузить 0 @@: pop eax ret endp ;Приступая к истолкованию следующей процедуры, следует разобраться с fpu-форматом. ;Сопроцессор подерживает несколько типов данных, которые перед исполнением всегда ;преобразуются в конечный 80 битный = 10 байтный вид. Подобно десятичной записи ;(123.123 = 1*10^2 + 2*10^1 + 3*10^0 + 1*10^-1 + 2*10^-2 + 3*10^-3 = 1.23123*10^2), ;эти 80 бит имеют специальную область под мантиссу(0..63), указатель степени(64..78), ;знак числа(79): ;0'000000000000000'0000000000000000000000000000000000000000000000000000000000000000b ;| | |поле мантиссы ;| | ;| |поле экспоненты(000000000000000b(-16383d)..011111111111111b(0d)..111111111111111b(+16384d)) ;| ;поле знака(0b = "+", 1b = "-") ; ;Приведем 123.123 к двоичному виду, для этого нужно кадую цифру вынести за знак запятой(точки), ;т.е. число до запятой(логарифм которого за основанием с-мы счисления>0) делить на два, и наоборот: ;дробную часть числа(логарифм которой за основанием счисления<0) - умножать на 2: ;123/2 1 <- младший остаток от деления ;061/2 1 ;030/2 0 ;015/2 1 ;007/2 1 ;003/2 1 ;001/2 1 <- старший остаток от деления ;Складываем остатки(запутались, попробуйте то же проделать с 123, делив на 10) с конца: ;1111011b ;Переведем обратно: 1*2^6 + 1*2^5 + 1*2^4 + 1*2^3 + 0*2^2 + 1*2^1 + 1*2^0 = ; = 64 + 32 + 16 + 8 + 0 + 2 + 1 = ; = 123 ;Поиграем с точкой(запятой): ;1111011b = 1.111011b*2^6 = 11.11011b*2^5 ... ;Хотя-бы 1.111011b*2^6 проверим: (1*2^0 + 1*2^-1 + 1*2^-2 + 1*2^-3 + 0*2^-4 + 1*2^-5 + 1*2^-6)*2^6 = ; = (1 + 1/2 + 1/4 + 1/8 + 0 + 1/32 + 1/64 )*64 = ; = 64 + 32 + 16 + 8 + 0 + 2 + 1 = ; = 123 ;Продолжим... 0.123: ; .123*2 ;0.246*2 ;0.492*2 ;0.984*2 ;1.968*2 ;1.936*2 ;... ;Складываем остатки(запутались вновь, умножайте 0.123 на 10) с начала: ; 00011b... ;Наконец лепим целую и дробную часть: 1111011.00011b... ;Нормализируем число: 1.11101100011b <- сдвинули на 6 позиций левее ;Мантисса готова, пишем експоненту: 011111111111111b + 110b = 100000000000101b. ;Готово! ; ;В случае преобразования только 0.123: 00011b... <- это и есть Denormal\"ненормальный *)". ;Нормализируем: 1.1b... * 2^-4. ;Готово! ; ; ; ;Ура, добрались. Так зачем нам эта fxtractx? Проблема в "плохом" отношении процессора к числам ;до->$0001'8000000000000000..$7FFE'FFFFFFFFFFFFFFFF<-после. Прока игнорит допустимые кордоны ;и тупо выдирает мантиссу в st, степень двойки в st1, расширяя пространство значений. proc fxtractx uses eax ebx ecx edx pushfd ;сохранить флаги sub esp,10 ;тут будем ковырсаться fstp tbyte[esp] ;извлекаем число pop ebx edx ;edx:ebx = мантисса xor eax,eax ;считаем ее нормализированной mov ecx,eax bsr ecx,edx ;ищем единицу jz @f ;возможно число очень маленькое или 0 neg ecx add ecx,31 ;позиция еденицы относительно 31 бита sub eax,ecx ;уменьшить степень двойки(експоненту) shld edx,ebx,cl ;выплюнуть входные нули shl ebx,cl ;подтянуть младшую часть мантиссы jmp .significand_found ;мантиссу нормализировали @@: cmp ebx,ecx jz .significand_found ;т.е. мантисса = 0 mov edx,ebx ;подсунуть младшую часть мантиссы mov ebx,ecx ;обнулить бышую младшую bsr ecx,edx ;пробуем оптимизировать neg ecx add ecx,31 ;шаг shl edx,cl ;оптиимзации neg ecx lea eax,[eax-32+ecx +1];-62 максимум .significand_found: mov ecx,$7ffe0000 ;узнаем знак числа, $3fff = 0(см. вступление) pop cx ;получить знак и експоненту числа shl cx,1 ;получить знак числа rcr ecx,1 ;установить $3fff(+0) или $bfff(-0), убрать бит знака с експоненты числа ror ecx,16 ;записать последние 16 бит нормализированной мантиссы push cx edx ebx ;... fld tbyte[esp] ;загрузить ее shr ecx,16 ;вспоминаем експоненту входного числа lea eax,[eax+ecx-$3fff];корректируем ее согласно проделаным подтягиваниям(shl) mov [esp],eax fild dword[esp] ;грузим fxch ;делаем словно fxtract add esp,10 ;освобождаем стек popfd ;восстанавливаем влаги ret ;до скорой встречи endp
!Сочетанием отмечены поправки ";;" Код (Text): proc fxtractx uses eax ebx ecx edx pushfd ;сохранить флаги sub esp,10 ;тут будем ковырсаться fstp tbyte[esp] ;извлекаем число pop ebx edx ;edx:ebx = мантисса xor eax,eax ;считаем ее нормализированной mov ecx,eax bsr ecx,edx ;ищем единицу jz @f ;возможно число очень маленькое или 0 neg ecx add ecx,31 ;позиция еденицы относительно 31 бита sub eax,ecx ;уменьшить степень двойки(експоненту) shld edx,ebx,cl ;выплюнуть входные нули shl ebx,cl ;подтянуть младшую часть мантиссы jmp .significand_found ;мантиссу нормализировали @@: cmp ebx,ecx jz .significand_found ;т.е. мантисса = 0 mov edx,ebx ;подсунуть младшую часть мантиссы mov ebx,ecx ;обнулить бышую младшую bsr ecx,edx ;пробуем оптимизировать neg ecx add ecx,31 ;шаг shl edx,cl ;оптиимзации neg ecx ;; lea eax,[eax-32+ecx] ;-63 максимум ;; .significand_found: mov ecx,$7ffe0000 ;узнаем знак числа, $3fff = 0(см. вступление) pop cx ;получить знак и експоненту числа shl cx,1 ;получить знак числа rcr ecx,1 ;установить $3fff(+0) или $bfff(-0), убрать бит знака с експоненты числа ;; cmp cx,1 adc cx,0 ;; ror ecx,16 ;записать последние 16 бит нормализированной мантиссы push cx edx ebx ;... fld tbyte[esp] ;загрузить ее shr ecx,16 ;вспоминаем експоненту входного числа lea eax,[eax+ecx-$3fff];корректируем ее согласно проделаным подтягиваниям(shl) mov [esp],eax fild dword[esp] ;грузим fxch ;делаем словно fxtract add esp,10 ;освобождаем стек popfd ;восстанавливаем влаги ret ;до скорой встречи endp