Преобразование строки в вещественное число на Ассемблере
Про преобразование строки в обычное число я уже писал, теперь задача посложнее - надо преобразовать строку в вещественное число с плавающей запятой (float). Для этого я написал вот такую функцию преобразования:Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------
- ; Функция преобразования строки в вещественное число
- ; 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
Code (Assembler) : Убрать нумерацию
- ; Сегмент данных
- 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
Просмотров: 9189 | Комментариев: 13
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
stalker
(23.04.2022 в 09:58):
Большое спасибо!
Mixer
(09.01.2014 в 23:58):
А не имеется ли в залежах функции "обратной" данной? Из вещественного в строку! Без использования платформозависимого API/ Если имеется не моглы бы вы поделиться? (dr [dot] vampir [гав-гав] rambler [dot] ru)
ManHunter
(03.06.2012 в 21:21):
kw33, разве синтаксис языков программирования зависит от локали пользователя?
Балбес
(02.06.2012 в 19:13):
После 199-й строки вставить:
cmp al,','
je .loc_complete_after_dot
После 141-й строки вставить
cmp al,','
je .loc_complete_before_dot
После 56-й вставить
cmp al,','
je .loc_chk_dot
Усё, теперь принимает и точку, и запятую. Если нужно, чтобы принимало только запятую, просто замените все три '.' на ','
cmp al,','
je .loc_complete_after_dot
После 141-й строки вставить
cmp al,','
je .loc_complete_before_dot
После 56-й вставить
cmp al,','
je .loc_chk_dot
Усё, теперь принимает и точку, и запятую. Если нужно, чтобы принимало только запятую, просто замените все три '.' на ','
kw33
(01.06.2012 в 14:57):
Дело не в языках программирования. В России - десятичная запятая, у америкосов - точка.
ManHunter
(31.05.2012 в 14:29):
А списки и объекты тогда через что определять?
какой-нибудь Object fuck = {3.14, 4.25, 5.36}
какой-нибудь Object fuck = {3.14, 4.25, 5.36}
Voffka
(31.05.2012 в 12:24):
А вот ты фиг угадал, в Visual Studio в зависимости от региона флот определяется по разному и приходится либо насильно делать америку, либо переводить в строку и менять запятую на точку.
UNNAME
(31.05.2012 в 09:47):
Спасибо...
Балбес
(30.05.2012 в 23:59):
Точно, извините. Паскаль сам в глаза не видел уже года три. Сейчас я вспомнил, что в паскалевской str2float при обработке нужно было преобразовывать точку в запятую, иначе это ругалось и падало. а в самом языке разделитель - точка.
ManHunter
(30.05.2012 в 20:13):
У нас был какой-то разный Паскаль?
Pascal: http://pas1.ru/real
Delphi: http://www.interface.ru/home.asp?artId=2561
Последний раз компилятор Turbo Pascal я запускал лет 10 назад, но точно помню, что везде в синтаксисе дробная часть отделяется точкой. То же самое в SQL, JavaScript, PHP, Fortran, Forth, Python и во всех Бейсиках. С какими-то другими языками на практике не сталкивался, но более чем уверен, что везде дробная часть отделяется точкой.
Pascal: http://pas1.ru/real
Delphi: http://www.interface.ru/home.asp?artId=2561
Последний раз компилятор Turbo Pascal я запускал лет 10 назад, но точно помню, что везде в синтаксисе дробная часть отделяется точкой. То же самое в SQL, JavaScript, PHP, Fortran, Forth, Python и во всех Бейсиках. С какими-то другими языками на практике не сталкивался, но более чем уверен, что везде дробная часть отделяется точкой.
Балбес
(30.05.2012 в 19:41):
Ну, я, как приверженец С++, с вами согласен. Но ведь есть языки, разделяющие целую и дробную часть запятой - да хоть тот же паскаль. Иначе о какой универсальности может идти речь?
ManHunter
(30.05.2012 в 11:54):
В программировании нет и никогда не будет разделения числа запятой. Дробное число разделяется точкой, а запятой разделяется список. И никак иначе.
Балбес
(30.05.2012 в 11:52):
А если число будет разделено не точкой, а запятой?
Добавить комментарий
Заполните форму для добавления комментария