Про преобразование строки в обычное число я уже писал, теперь задача посложнее - надо преобразовать строку в вещественное число с плавающей запятой (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
В приложении пример программы с исходным текстом, использующей функцию преобразования строки в вещественное число.