Преобразование строки в вещественное число на Ассемблере

Про преобразование строки в обычное число я уже писал, теперь задача посложнее - надо преобразовать строку в вещественное число с плавающей запятой (float). Для этого я написал вот такую функцию преобразования:
;------------------------------------------------------------
; Функция преобразования строки в вещественное число
; by ManHunter / PCL
; http://www.manhunter.ru
;
; Параметры:
;   lpStr - указатель на исходную строку в формате ASCIIZ
;   lpResult - указатель на переменную-приемник значения
; На выходе:
;   EAX = 1 - строка успешно преобразована
;   EAX = 0 - строка не может быть преобразована в число
;
; Примечание: переменная-приемник может иметь размер DWORD
; или QWORD, в зависимости от этого должна изменяться и
; функция (см. комментарии в конце кода). По умолчанию
; считается, что переменная имеет размер DWORD
;------------------------------------------------------------
proc    string2float lpStr:DWORD, lpResult:DWORD
        ; Локальные переменные
        locals
          dot    dd ?   ; Указатель на дробную часть
          exp    dd ?   ; Указатель на экспоненту
 
          digit  dd ?   ; Цифра
        endl
 
        pusha
 
        ; Проверка строки на валидность
        mov     [digit],1
 
        mov     [exp],0
        mov     [dot],0
        mov     esi,[lpStr]
        ; Минус или плюс может быть только в начале
        cmp     byte [esi],'-'
        je      @f
        cmp     byte [esi],'+'
        jne     .loc_chk_loop
@@:
        inc     esi
 
        ; После знака не может быть нуля
        cmp     byte [esi],0
        je      .loc_chk_error
.loc_chk_loop:
        ; В строке должны быть цифр, экспонента и не более одной точки
        lodsb
        or      al,al
        jz      .loc_chk_complete
        cmp     al,'e'
        je      .loc_chk_exp
        cmp     al,'E'
        je      .loc_chk_exp
        cmp     al,'.'
        je      .loc_chk_dot
        cmp     al,'0'
        jb      .loc_chk_error
        cmp     al,'9'
        ja      .loc_chk_error
        jmp     .loc_chk_loop
 
.loc_chk_dot:
        ; Точка в строке уже есть?
        cmp     [dot],0
        ; Строка имеет некорректный формат
        jne     .loc_chk_error
 
        ; Экспонента уже есть?
        cmp     [exp],0
        ; Строка имеет некорректный формат
        jne     .loc_chk_error
 
        ; Указатель на дробную часть
        mov     [dot],esi
 
        jmp     .loc_chk_loop
 
.loc_chk_exp:
        ; Экспонента уже есть?
        cmp     [exp],0
        ; Строка имеет некорректный формат
        jne     .loc_chk_error
 
        ; Указатель на начало экспоненты
        mov     [exp],esi
 
        ; Сразу после экспоненты не может быть нуля
        cmp     byte [esi],0
        je      .loc_chk_error
 
        ; После экспоненты может быть знак
        cmp     byte [esi],'-'
        je      @f
        cmp     byte [esi],'+'
        jne     .loc_chk_loop
@@:
        inc     esi
 
        ; Сразу после минуса не может быть нуля
        cmp     byte [esi],0
        je      .loc_chk_error
 
        ; Проверить следующий символ
        jmp     .loc_chk_loop
 
.loc_chk_error:
        ; Строка не является числом
        mov     [digit],0
        jmp     .loc_ret
 
.loc_chk_complete:
        ; Инициализация сопроцессора
        finit
 
        ; Начальное значение числа
        fldz
 
        ; Множитель и делитель
        mov     [digit],10
        fild    dword [digit]
 
        ; Запись значений до запятой
        mov     esi,[lpStr]
 
        ; В начале строки минус?
        cmp     byte [esi],'-'
        je      @f
        cmp     byte [esi],'+'
        jne     .loc_before_dot
@@:
        inc     esi
        ; Преобразование числа до запятой
.loc_before_dot:
        lodsb
        ; Конец строки?
        or      al,al
        jz      .loc_complete
 
        cmp     al,'.'
        je      .loc_complete_before_dot
        cmp     al,'e'
        je      .loc_exp
        cmp     al,'E'
        je      .loc_exp
 
        ; Очередная цифра
        sub     al,'0'
        movzx   eax,al
        mov     [digit],eax
 
        ; Записать
        fild    dword [digit]
        fxch    st2
        fmul    st0,st1
        fxch    st2
        fadd    st2,st0
 
        ffree   st0     ; Почистить стек
        fincstp
 
        jmp     .loc_before_dot
 
        ; Преобразование дробной части числа
.loc_complete_before_dot:
        ; Дробная часть есть?
        cmp     [dot],0
        je      .loc_complete_after_dot
 
        ; Экспонента есть?
        cmp     [exp],0
        je      @f
 
        ; Указатель на начало экспоненты
        mov     esi,[exp]
        jmp     .loc_start_after_dot
@@:
        ; Иначе перенести указатель на конец строки
        xor     ecx,ecx
        dec     ecx
        xor     eax,eax
        mov     edi,esi
        repne   scasb
 
        mov     esi,edi
 
.loc_start_after_dot:
        std
        dec     esi
        dec     esi
 
        ; Дробная часть
        fldz
        fxch    st1
.loc_after_dot:
        lodsb
        ; Конец дробной части?
        cmp     al,'.'
        je      .loc_complete_after_dot
 
        ; Очередная цифра
        sub     al,'0'
        movzx   eax,al
        mov     [digit],eax
 
        ; Записать
        fild    dword [digit]
        fadd    st2,st0
        fxch    st2
        fdiv    st0,st1
        fxch    st2
 
        ffree   st0     ; Почистить стек
        fincstp
 
        jmp     .loc_after_dot
 
.loc_complete_after_dot:
        ; Сбросить флаг направления
        cld
 
        ffree   st0     ; Почистить стек
        fincstp
 
        ; Сложить дробную и целую часть
        fadd    st1,st0
 
.loc_exp:
        ; Экспонента есть?
        cmp     [exp],0
        je      .loc_complete
 
        ; Получить значение экспоненты
        xor     ecx,ecx
 
        mov     esi,[exp]
        ; В начале строки минус?
        cmp     byte [esi],'-'
        je      @f
        cmp     byte [esi],'+'
        jne     .loc_start_exp
@@:
        inc     esi
.loc_start_exp:
        lodsb
        or      al,al
        jz      .loc_end_exp
 
        sub     al,'0'
        movzx   eax,al
        imul    ecx,10
        add     ecx,eax
 
        jmp     .loc_start_exp
.loc_end_exp:
 
        or      ecx,ecx
        jz      .loc_complete
 
        ffree   st0     ; Почистить стек
        fincstp
 
        mov     [digit],10
        fild    dword [digit]
 
        ; Делить или умножать?
        mov     esi,[exp]
        cmp     byte [esi],'-'
        je      .loc_exp_divide
 
.loc_exp_multiple:
        fmul    st1,st0
        loop    .loc_exp_multiple
        jmp     .loc_complete
 
.loc_exp_divide:
        fdiv    st1,st0
        loop    .loc_exp_divide
 
.loc_complete:
        ffree   st0     ; Почистить стек
        fincstp
 
        ; В начале строки минус?
        mov     esi,[lpStr]
        cmp     byte [esi],'-'
        jne     @f
 
        ; Изменить знак числа
        fchs
@@:
        ; Записать значение в ячейку памяти
        mov     eax,[lpResult]
        ; Если требуется повышенная точность, то приемник
        ; должен иметь размер QWORD, а следующую команду
        ; надо заменить на fstp qword [eax]
        fstp    dword [eax]
 
        ; Успешное преобразование
        mov     [digit],1
.loc_ret:
        popa
 
        ; Результат преобразования
        mov     eax,[digit]
 
        ret
endp

Функция универсальная, понимает положительные и отрицательные, нормализованные и денормализованные числа, с любой мантиссой и экспонентой. Например, "123.45", "-1.67e+4", "99.999e-99", "45e004" и т.п. Также выполняется проверка на корректность исходной строки. Заведения дополнительных переменных в сегменте данных не требуется. Параметры вызова: lpStr - указатель на строку для преобразования, lpResult - указатель на переменную-приемник для записи результата, ее размер может быть DWORD или QWORD, в зависимости от желаемой точности результата. Соответственно, вы должны изменить команду (строка 297 в листинге) на fstp dword
 или fstp qword [eax].
[ASM]; Сегмент данных
section '.data' data readable writeable
...
flt     db '-15.245e+6',0  ; Исходная строка
result  dq ?               ; Переменная для хранения результата (QWORD)
 
; Сегмент кода
section '.code' code readable executable
...
        ; Преобразовать строку в вещественное число
        stdcall string2float,flt,result
        ; Теперь в [result] записано число -1.524500e+07

В приложении пример программы с исходным текстом, использующей функцию преобразования строки в вещественное число.
Теги:
float
Добавлено: 14 Сентября 2013 03:20:11 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...