Blog. Just Blog

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

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Про преобразование строки в обычное число я уже писал, теперь задача посложнее - надо преобразовать строку в вещественное число с плавающей запятой (float). Для этого я написал вот такую функцию преобразования:
  1. ;------------------------------------------------------------
  2. ; Функция преобразования строки в вещественное число
  3. ; by ManHunter / PCL
  4. ; http://www.manhunter.ru
  5. ;
  6. ; Параметры:
  7. ;   lpStr - указатель на исходную строку в формате ASCIIZ
  8. ;   lpResult - указатель на переменную-приемник значения
  9. ; На выходе:
  10. ;   EAX = 1 - строка успешно преобразована
  11. ;   EAX = 0 - строка не может быть преобразована в число
  12. ;
  13. ; Примечание: переменная-приемник может иметь размер DWORD
  14. ; или QWORD, в зависимости от этого должна изменяться и
  15. ; функция (см. комментарии в конце кода). По умолчанию
  16. ; считается, что переменная имеет размер DWORD
  17. ;------------------------------------------------------------
  18. proc    string2float lpStr:DWORD, lpResult:DWORD
  19.         ; Локальные переменные
  20.         locals
  21.           dot    dd ?   ; Указатель на дробную часть
  22.           exp    dd ?   ; Указатель на экспоненту
  23.  
  24.           digit  dd ?   ; Цифра
  25.         endl
  26.  
  27.         pusha
  28.  
  29.         ; Проверка строки на валидность
  30.         mov     [digit],1
  31.  
  32.         mov     [exp],0
  33.         mov     [dot],0
  34.         mov     esi,[lpStr]
  35.         ; Минус или плюс может быть только в начале
  36.         cmp     byte [esi],'-'
  37.         je      @f
  38.         cmp     byte [esi],'+'
  39.         jne     .loc_chk_loop
  40. @@:
  41.         inc     esi
  42.  
  43.         ; После знака не может быть нуля
  44.         cmp     byte [esi],0
  45.         je      .loc_chk_error
  46. .loc_chk_loop:
  47.         ; В строке должны быть цифр, экспонента и не более одной точки
  48.         lodsb
  49.         or      al,al
  50.         jz      .loc_chk_complete
  51.         cmp     al,'e'
  52.         je      .loc_chk_exp
  53.         cmp     al,'E'
  54.         je      .loc_chk_exp
  55.         cmp     al,'.'
  56.         je      .loc_chk_dot
  57.         cmp     al,'0'
  58.         jb      .loc_chk_error
  59.         cmp     al,'9'
  60.         ja      .loc_chk_error
  61.         jmp     .loc_chk_loop
  62.  
  63. .loc_chk_dot:
  64.         ; Точка в строке уже есть?
  65.         cmp     [dot],0
  66.         ; Строка имеет некорректный формат
  67.         jne     .loc_chk_error
  68.  
  69.         ; Экспонента уже есть?
  70.         cmp     [exp],0
  71.         ; Строка имеет некорректный формат
  72.         jne     .loc_chk_error
  73.  
  74.         ; Указатель на дробную часть
  75.         mov     [dot],esi
  76.  
  77.         jmp     .loc_chk_loop
  78.  
  79. .loc_chk_exp:
  80.         ; Экспонента уже есть?
  81.         cmp     [exp],0
  82.         ; Строка имеет некорректный формат
  83.         jne     .loc_chk_error
  84.  
  85.         ; Указатель на начало экспоненты
  86.         mov     [exp],esi
  87.  
  88.         ; Сразу после экспоненты не может быть нуля
  89.         cmp     byte [esi],0
  90.         je      .loc_chk_error
  91.  
  92.         ; После экспоненты может быть знак
  93.         cmp     byte [esi],'-'
  94.         je      @f
  95.         cmp     byte [esi],'+'
  96.         jne     .loc_chk_loop
  97. @@:
  98.         inc     esi
  99.  
  100.         ; Сразу после минуса не может быть нуля
  101.         cmp     byte [esi],0
  102.         je      .loc_chk_error
  103.  
  104.         ; Проверить следующий символ
  105.         jmp     .loc_chk_loop
  106.  
  107. .loc_chk_error:
  108.         ; Строка не является числом
  109.         mov     [digit],0
  110.         jmp     .loc_ret
  111.  
  112. .loc_chk_complete:
  113.         ; Инициализация сопроцессора
  114.         finit
  115.  
  116.         ; Начальное значение числа
  117.         fldz
  118.  
  119.         ; Множитель и делитель
  120.         mov     [digit],10
  121.         fild    dword [digit]
  122.  
  123.         ; Запись значений до запятой
  124.         mov     esi,[lpStr]
  125.  
  126.         ; В начале строки минус?
  127.         cmp     byte [esi],'-'
  128.         je      @f
  129.         cmp     byte [esi],'+'
  130.         jne     .loc_before_dot
  131. @@:
  132.         inc     esi
  133.         ; Преобразование числа до запятой
  134. .loc_before_dot:
  135.         lodsb
  136.         ; Конец строки?
  137.         or      al,al
  138.         jz      .loc_complete
  139.  
  140.         cmp     al,'.'
  141.         je      .loc_complete_before_dot
  142.         cmp     al,'e'
  143.         je      .loc_exp
  144.         cmp     al,'E'
  145.         je      .loc_exp
  146.  
  147.         ; Очередная цифра
  148.         sub     al,'0'
  149.         movzx   eax,al
  150.         mov     [digit],eax
  151.  
  152.         ; Записать
  153.         fild    dword [digit]
  154.         fxch    st2
  155.         fmul    st0,st1
  156.         fxch    st2
  157.         fadd    st2,st0
  158.  
  159.         ffree   st0     ; Почистить стек
  160.         fincstp
  161.  
  162.         jmp     .loc_before_dot
  163.  
  164.         ; Преобразование дробной части числа
  165. .loc_complete_before_dot:
  166.         ; Дробная часть есть?
  167.         cmp     [dot],0
  168.         je      .loc_complete_after_dot
  169.  
  170.         ; Экспонента есть?
  171.         cmp     [exp],0
  172.         je      @f
  173.  
  174.         ; Указатель на начало экспоненты
  175.         mov     esi,[exp]
  176.         jmp     .loc_start_after_dot
  177. @@:
  178.         ; Иначе перенести указатель на конец строки
  179.         xor     ecx,ecx
  180.         dec     ecx
  181.         xor     eax,eax
  182.         mov     edi,esi
  183.         repne   scasb
  184.  
  185.         mov     esi,edi
  186.  
  187. .loc_start_after_dot:
  188.         std
  189.         dec     esi
  190.         dec     esi
  191.  
  192.         ; Дробная часть
  193.         fldz
  194.         fxch    st1
  195. .loc_after_dot:
  196.         lodsb
  197.         ; Конец дробной части?
  198.         cmp     al,'.'
  199.         je      .loc_complete_after_dot
  200.  
  201.         ; Очередная цифра
  202.         sub     al,'0'
  203.         movzx   eax,al
  204.         mov     [digit],eax
  205.  
  206.         ; Записать
  207.         fild    dword [digit]
  208.         fadd    st2,st0
  209.         fxch    st2
  210.         fdiv    st0,st1
  211.         fxch    st2
  212.  
  213.         ffree   st0     ; Почистить стек
  214.         fincstp
  215.  
  216.         jmp     .loc_after_dot
  217.  
  218. .loc_complete_after_dot:
  219.         ; Сбросить флаг направления
  220.         cld
  221.  
  222.         ffree   st0     ; Почистить стек
  223.         fincstp
  224.  
  225.         ; Сложить дробную и целую часть
  226.         fadd    st1,st0
  227.  
  228. .loc_exp:
  229.         ; Экспонента есть?
  230.         cmp     [exp],0
  231.         je      .loc_complete
  232.  
  233.         ; Получить значение экспоненты
  234.         xor     ecx,ecx
  235.  
  236.         mov     esi,[exp]
  237.         ; В начале строки минус?
  238.         cmp     byte [esi],'-'
  239.         je      @f
  240.         cmp     byte [esi],'+'
  241.         jne     .loc_start_exp
  242. @@:
  243.         inc     esi
  244. .loc_start_exp:
  245.         lodsb
  246.         or      al,al
  247.         jz      .loc_end_exp
  248.  
  249.         sub     al,'0'
  250.         movzx   eax,al
  251.         imul    ecx,10
  252.         add     ecx,eax
  253.  
  254.         jmp     .loc_start_exp
  255. .loc_end_exp:
  256.  
  257.         or      ecx,ecx
  258.         jz      .loc_complete
  259.  
  260.         ffree   st0     ; Почистить стек
  261.         fincstp
  262.  
  263.         mov     [digit],10
  264.         fild    dword [digit]
  265.  
  266.         ; Делить или умножать?
  267.         mov     esi,[exp]
  268.         cmp     byte [esi],'-'
  269.         je      .loc_exp_divide
  270.  
  271. .loc_exp_multiple:
  272.         fmul    st1,st0
  273.         loop    .loc_exp_multiple
  274.         jmp     .loc_complete
  275.  
  276. .loc_exp_divide:
  277.         fdiv    st1,st0
  278.         loop    .loc_exp_divide
  279.  
  280. .loc_complete:
  281.         ffree   st0     ; Почистить стек
  282.         fincstp
  283.  
  284.         ; В начале строки минус?
  285.         mov     esi,[lpStr]
  286.         cmp     byte [esi],'-'
  287.         jne     @f
  288.  
  289.         ; Изменить знак числа
  290.         fchs
  291. @@:
  292.         ; Записать значение в ячейку памяти
  293.         mov     eax,[lpResult]
  294.         ; Если требуется повышенная точность, то приемник
  295.         ; должен иметь размер QWORD, а следующую команду
  296.         ; надо заменить на fstp qword [eax]
  297.         fstp    dword [eax]
  298.  
  299.         ; Успешное преобразование
  300.         mov     [digit],1
  301. .loc_ret:
  302.         popa
  303.  
  304.         ; Результат преобразования
  305.         mov     eax,[digit]
  306.  
  307.         ret
  308. endp
Функция универсальная, понимает положительные и отрицательные, нормализованные и денормализованные числа, с любой мантиссой и экспонентой. Например, "123.45", "-1.67e+4", "99.999e-99", "45e004" и т.п. Также выполняется проверка на корректность исходной строки. Заведения дополнительных переменных в сегменте данных не требуется. Параметры вызова: lpStr - указатель на строку для преобразования, lpResult - указатель на переменную-приемник для записи результата, ее размер может быть DWORD или QWORD, в зависимости от желаемой точности результата. Соответственно, вы должны изменить команду (строка 297 в листинге) на fstp dword [eax] или fstp qword [eax].
  1. ; Сегмент данных
  2. section '.data' data readable writeable
  3. ...
  4. flt     db '-15.245e+6',0  ; Исходная строка
  5. result  dq ?               ; Переменная для хранения результата (QWORD)
  6.  
  7. ; Сегмент кода
  8. section '.code' code readable executable
  9. ...
  10.         ; Преобразовать строку в вещественное число
  11.         stdcall string2float,flt,result
  12.         ; Теперь в [result] записано число -1.524500e+07
В приложении пример программы с исходным текстом, использующей функцию преобразования строки в вещественное число.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

String.To.Float.Demo.zip (3,028 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 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
Усё, теперь принимает и точку, и запятую. Если нужно, чтобы принимало только запятую, просто замените все три '.' на ','
kw33 (01.06.2012 в 14:57):
Дело не в языках программирования. В России - десятичная запятая, у америкосов - точка.
ManHunter (31.05.2012 в 14:29):
А списки и объекты тогда через что определять?
какой-нибудь 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 и во всех Бейсиках. С какими-то другими языками на практике не сталкивался, но более чем уверен, что везде дробная часть отделяется точкой.
Балбес (30.05.2012 в 19:41):
Ну, я, как приверженец С++, с вами согласен. Но ведь есть языки, разделяющие целую и дробную часть запятой - да хоть тот же паскаль. Иначе о какой универсальности может идти речь?
ManHunter (30.05.2012 в 11:54):
В программировании нет и никогда не будет разделения числа запятой. Дробное число разделяется точкой, а запятой разделяется список. И никак иначе.
Балбес (30.05.2012 в 11:52):
А если число будет разделено не точкой, а запятой?

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0057 сек.) / Память: 4.5 Mb
Наверх