Blog. Just Blog

Быстрый поиск

Введите фрагмент названия статьи для поиска

Преобразование числа в строку с разделением на разряды

17.01.2016 | Категория: Образ мышления: Assembler | Автор: ManHunter

Преобразование числа в строку с разделением на разряды

Если вы работаете с большими десятичными числами, то наверняка согласитесь, что число с разделением на разряды (то есть с группировкой по три символа: тысячи, миллионы и так далее) воспринимается гораздо лучше, чем просто последовательность цифр. Так проще выявлять ошибки или, например, с одного взгляда можно оценить порядок числа.

Читать статью целиком »
Просмотров: 5176 | Комментариев: 9

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

14.03.2014 | Категория: Образ мышления: Assembler | Автор: ManHunter
Ранее я выкладывал статью о том, как на Ассемблере перевести строку в вещественное число, теперь настало время произвести обратную операцию, то есть перевести вещественное число в строку. В отличие от целых чисел, где достаточно нескольких строк кода, с преобразованием вещественных чисел пришлось повозиться. В результате появилась следующая функция. За основу взята аналогичная функция из пакета MASM, но со значительными доработками.
  1. ;------------------------------------------------------------
  2. ; Функция перевода вещественного числа в строку
  3. ;------------------------------------------------------------
  4. ; Портирование с MASM, доработка и оптимизация - ManHunter
  5. ; Параметры:
  6. ;   lpFloat - указатель на вещественное число TBYTE
  7. ;   lpResult - указатель на строку-приемник результата
  8. ;------------------------------------------------------------
  9. proc    FloatToString lpFloat:DWORD, lpResult:DWORD
  10.         ; Локальные переменные
  11.         local   digits_count:DWORD
  12.         local   tmp:DWORD
  13.         local   old_cw:WORD
  14.         local   new_cw:WORD
  15.         local   saved_float:TBYTE
  16.         local   tmp1 rb 12h
  17.         local   tmp2 rb 12h
  18.  
  19.         ; Сохранить все регистры
  20.         pusha
  21.  
  22.         ; Указатель на строку-приемник
  23.         mov     edi,[lpResult]
  24.  
  25.         ; Это ноль?
  26.         mov     esi,[lpFloat]
  27.         cmp     dword [esi],0
  28.         jne     loc_not_zero
  29.         cmp     dword [esi+4],0
  30.         jne     loc_not_zero
  31.         cmp     word [esi+8],0x8000
  32.         je      loc_minus_zero
  33.         cmp     word [esi+8],0
  34.         jne     loc_not_zero
  35.         ; Записать в строку ноль
  36.         mov     al,'0'
  37.         stosb
  38.         jmp     loc_ret
  39. loc_minus_zero:
  40.         ; Записать в строку минус ноль
  41.         mov     ax,'-0'
  42.         stosw
  43.         jmp     loc_ret
  44. loc_not_zero:
  45.         ; Denormalized?
  46.         mov     ax,word [esi+8]
  47.         and     ax,7F80h
  48.         or      ax,ax
  49.         jnz     loc_not_denorm
  50.         cmp     dword [esi],0
  51.         jne     loc_denorm
  52.         cmp     dword [esi+4],0
  53.         je      loc_not_denorm
  54. loc_denorm:
  55.         mov     esi,szDen
  56.         movsd
  57.         movsd
  58.         movsd
  59.         jmp     loc_ret
  60. loc_not_denorm:
  61.         ; Infinity или NaN?
  62.         mov     ax,word [esi+8]
  63.         and     ax,7F80h
  64.         cmp     ax,7F80h
  65.         jne     loc_not_inf_nan
  66.  
  67.         ; Это Infinity?
  68.         cmp     dword [esi],0
  69.         jne     loc_nan
  70.         cmp     dword [esi+4],80000000h
  71.         jne     loc_nan
  72. loc_infinity:
  73.         mov     esi,szInf
  74.         movsd
  75.         movsb
  76. loc_plus_minus:
  77.         movsd
  78.         mov     esi,[lpFloat]
  79.         test    byte [esi+9],80h
  80.         jz      loc_ret
  81.         mov     esi,[lpResult]
  82.         mov     byte[esi],'-'
  83.         jmp     loc_ret
  84. loc_nan:
  85.         mov     esi,szNan
  86.         jmp     loc_plus_minus
  87.  
  88. loc_not_inf_nan:
  89.         ; Скопировать число в локальную переменную
  90.         push    edi
  91.         mov     esi,[lpFloat]
  92.         lea     edi,[saved_float]
  93.         movsd
  94.         movsd
  95.         movsw
  96.         pop     edi
  97.         ; Число отрицательное?
  98.         cmp     dword [saved_float+6],0
  99.         jge     loc_not_signed
  100.         ; Привести число к абсолютному значению
  101.         and     byte [saved_float+9],7Fh
  102.         ; Записать в строку минус
  103.         mov     al,'-'
  104.         stosb
  105.  
  106. loc_not_signed:
  107.         ; Проверить число на наличие дробной части и
  108.         ; подсчитать количество цифр в нем
  109.         fclex
  110.         ; Сохранить управляющее слово
  111.         fstcw   [old_cw]
  112.         ; Установить управляющее слово
  113.         mov     [new_cw],0000001001111111b
  114.         fldcw   [new_cw]
  115.         lea     esi,[saved_float]
  116.         fld     tbyte [esi]
  117.         fld     st
  118.         ; Выделить мантиссу и порядок
  119.         fxtract
  120.         fstp    st
  121.         fldlg2
  122.         ; Получить количество цифр в числе
  123.         fmulp   st1,st
  124.         fistp   [digits_count]
  125.         ; Если цифр больше 16, то число отображается в
  126.         ; нормализованном виде с мантиссой и экспонентой
  127.         cmp     [digits_count],10h
  128.         jnb     loc_not_integer
  129.         ; У числа есть дробная часть?
  130.         fld     st
  131.         frndint
  132.         fcomp   st1
  133.         fstsw   ax
  134.         test    ah,01000000b
  135.         ; Да, отображать число с дробной частью
  136.         jz      loc_not_integer
  137.  
  138.         ; Целое число без дробной части и экспоненты
  139.         lea     eax,[tmp1]
  140.         fbstp   [eax]
  141.  
  142.         ; Перевести BCD-число в строку
  143.         push    edi
  144.         lea     esi,[tmp1+8]
  145.         lea     edi,[tmp2]
  146.         mov     ecx, 9
  147. @@:
  148.         std
  149.         xor     eax,eax
  150.         lodsb
  151.         cld
  152.         rol     ax,12
  153.         rol     ah,4
  154.         add     ax,'00'
  155.         stosw
  156.         loop    @b
  157.         pop     edi
  158.  
  159.         ; Пропустить лидирующий ноль
  160.         mov     eax,11h
  161.         mov     ecx,[digits_count]
  162.         sub     eax,ecx
  163.         inc     ecx
  164.         lea     esi,[tmp2+eax]
  165.         cmp     byte [esi],'0'
  166.         jne     @f
  167.         inc     esi
  168.         dec     ecx
  169. @@:
  170.         ; Перенести полученное число из временного буфера
  171.         rep     movsb
  172.         jmp     loc_clear_stack
  173.  
  174. loc_not_integer:
  175.         mov     eax,10h
  176.         sub     eax,[digits_count]
  177.  
  178.         ; Преобразовать число в целое до 16 разрядов
  179.         mov     ecx,eax
  180.         cmp     eax,0
  181.         jge     @f
  182.         neg     eax
  183. @@:
  184.         ; Для чисел больше 0 корректировка округления в сторону 0
  185.         mov     [new_cw],0000101001111111b
  186.         cmp     ecx,0
  187.         jge     @f
  188.         mov     [new_cw],0000011001111111b
  189. @@:
  190.         ; Установить управляющее слово
  191.         fldcw   [new_cw]
  192.  
  193.         ; Возвести 10 в степень количества цифр
  194.         mov     [tmp],eax
  195.         fild    [tmp]
  196.         fld     [float2]
  197.         fyl2x
  198.         fld1
  199.         fld     st1
  200.         fprem
  201.         f2xm1
  202.         faddp
  203.         fscale
  204.  
  205.         ; Почистить стек
  206.         fxch    st1
  207.         fstp    st
  208.  
  209.         ; Если число меньше 0, то умножить, иначе разделить
  210.         cmp     ecx,0
  211.         jge     @f
  212.         fdivp   st1,st
  213.         jmp     loc_rounded
  214. @@:
  215.         fmulp   st1,st
  216.  
  217. loc_rounded:
  218.         ; Полученное значение меньше 1.0e16 ?
  219.         fcom    [float1]
  220.         fstsw   ax
  221.         test    ah,1
  222.         jz      @f
  223.         fmul    [float2]
  224.         dec     [digits_count]
  225. @@:
  226.         ; Целое число без дробной части и экспоненты
  227.         lea     eax,[tmp1]
  228.         fbstp   [eax]
  229.  
  230.         ; Перевести BCD-число в строку
  231.         push    edi
  232.         lea     esi,[tmp1+8]
  233.         lea     edi,[tmp2]
  234.         mov     ecx, 9
  235. @@:
  236.         std
  237.         xor     eax,eax
  238.         lodsb
  239.         cld
  240.         rol     ax,12
  241.         rol     ah,4
  242.         add     ax,'00'
  243.         stosw
  244.         loop    @b
  245.         pop     edi
  246.  
  247.         ; Числу требуется мантисса и экспонента?
  248.         lea     esi,[tmp2+1]
  249.         mov     ecx,[digits_count]
  250.         cmp     ecx,-0Fh
  251.         jl      loc_mantiss_and_exponent
  252.         cmp     ecx,10h
  253.         jg      loc_mantiss_and_exponent
  254.  
  255.         ; Заполнить дробную часть числа
  256.         inc     ecx
  257.         cmp     ecx,0
  258.         jg      @f
  259.         mov     ax,'0.'
  260.         stosw
  261.         neg     ecx
  262.         mov     al,'0'
  263.         rep     stosb
  264.         mov     ecx,10h
  265.         jmp     loc_fraction_filled
  266. @@:
  267.         rep     movsb
  268.         mov     al,'.'
  269.         stosb
  270.         mov     ecx,10h
  271.         sub     ecx,[digits_count]
  272.  
  273. loc_fraction_filled:
  274.         rep     movsb
  275.         jmp     @f
  276.  
  277. loc_clear_fraction:
  278.         ; Удалить завершающие нули дробной части
  279.         dec     edi
  280. @@:
  281.         cmp     byte [edi-1],'0'
  282.         jz      loc_clear_fraction
  283.         cmp     byte [edi-1],'.'
  284.         jnz     @f
  285.         dec     edi
  286. @@:
  287.         jmp     loc_clear_stack
  288.  
  289. loc_mantiss_and_exponent:
  290.         ; Дробная часть мантиссы
  291.         movsb
  292.         mov     al,'.'
  293.         stosb
  294.         movsd
  295.         movsd
  296.         movsw
  297.         ; Удалить завершающие нули дробной части
  298. @@:
  299.         cmp     byte [edi-1],'0'
  300.         jne     @f
  301.         cmp     byte [edi-2],'.'
  302.         je      @f
  303.         dec     edi
  304.         jmp     @b
  305. @@:
  306.         ; Символ и знак экспоненты
  307.         mov     al,'e'
  308.         stosb
  309.         mov     al,'+'
  310.         mov     ebx,[digits_count]
  311.         cmp     ebx, 0
  312.         jge     @f
  313.         mov     al,'-'
  314.         neg     ebx
  315. @@:
  316.         stosb
  317.  
  318.         ; Значение экспоненты
  319.         mov     eax,ebx
  320.         mov     ecx,10
  321.         mov     ebx,4
  322. @@:
  323.         dec     ebx
  324.         xor     edx,edx
  325.         div     ecx
  326.         add     dl,'0'
  327.         mov     [tmp1+ebx],dl
  328.         or      ebx,ebx
  329.         jnz     @b
  330.  
  331.         ; Пропустить лидирующие нули экспоненты
  332.         mov     ecx,4
  333.         lea     esi,[tmp1]
  334. @@:
  335.         lodsb
  336.         cmp     al,'0'
  337.         jne     @f
  338.         dec     ecx
  339.         jmp     @b
  340. @@:
  341.         dec     esi
  342.         rep     movsb
  343.  
  344. loc_clear_stack:
  345.         ; Восстановить управляющее слово
  346.         fldcw   [old_cw]
  347. loc_ret:
  348.         ; Окончание строки
  349.         mov     al,0
  350.         stosb
  351.  
  352.         ; Восстановить все регистры
  353.         popa
  354.         ret
  355.  
  356. float1  dq      1.0e16
  357. float2  dq      10.0
  358.  
  359. szInf   db      '+Infinity'
  360. szNan   db      '+NaN'
  361. szDen   db      'Denormalized'
  362.  
  363. endp
Параметры вызова: lpFloat - указатель на вещественное число TBYTE (10 байт), lpResult - указатель на строку-приемник результата. Функция самодостаточная, не требует никаких дополнительных переменных в секции данных и вспомогательных функций, исходное число не меняется. В отличие от MASM'овской функции, поддерживаются вещественные числа в диапазоне -1e4932..+1e4932, увеличено количество отображаемых цифр после запятой, улучшена точность округления мантиссы для очень больших и очень маленьких чисел, улучшено форматирование значения экспоненты и выполнены другие действия по оптимизации алгоритма.

Читать статью целиком »
Просмотров: 11197 | Комментариев: 27

Оптимизация умножения на константу на Ассемблере

05.08.2013 | Категория: Образ мышления: Assembler | Автор: ManHunter
В предыдущей статье по оптимизации мы рассматривали замену команд деления, как это описано в "AMD Athlon Processor x86 Code Optimization Guide". Но в этом же мануале описана оптимизация умножения на константу. Тут нет универсальных алгоритмов, по которым можно было бы однозначно определить нужную последовательность команд замены, как в случае с делением. Для каждой константы набор команд определяется на основании их "весовых коэффициентов" и количества тактов процессора, необходимых для их выполнения. В качестве примера приведена вот такая табличка оптимизации умножения с 2 до 32. Ее всегда можно посмотреть и в самом мануале, но лучше пусть будет под рукой.
  1. ; Умножение на 2
  2. add reg1, reg1          ; 1 такт процессора
  3.  
  4. ; Умножение на 3
  5. lea reg1, [reg1+reg1*2] ; 2 такта процессора
  6.  
  7. ; Умножение на 4
  8. shl reg1, 2             ; 1 такт процессора
  9.  
  10. ; Умножение на 5
  11. lea reg1, [reg1+reg1*4] ; 2 такта процессора
  12.  
  13. ; Умножение на 6
  14. lea reg1, [reg1+reg1*2] ; 3 такта процессора
  15. add reg1, reg1
  16.  
  17. ; Умножение на 7: 
  18. mov reg2, reg1          ; 2 такта процессора
  19. shl reg1, 3
  20. sub reg1, reg2
  21.  
  22. ; Умножение на 8: 
  23. shl reg1, 3             ; 1 такт процессора
  24.  
  25. ; Умножение на 9: 
  26. lea reg1, [reg1+reg1*8] ; 2 такта процессора
  27.  
  28. ; Умножение на 10: 
  29. lea reg1, [reg1+reg1*4] ; 3 такта процессора
  30. add reg1, reg1
  31.  
  32. ; Умножение на 11:
  33. lea reg2, [reg1+reg1*8] ; 3 такта процессора
  34. add reg1, reg1
  35. add reg1, reg2
  36.  
  37. ; Умножение на 12:
  38. lea reg1, [reg1+reg1*2] ; 3 такта процессора
  39. shl reg1, 2
  40.  
  41. ; Умножение на 13:
  42. lea reg2, [reg1+reg1*2] ; 3 такта процессора
  43. shl reg1, 4
  44. sub reg1, reg2
  45.  
  46. ; Умножение на 14:
  47. lea reg2, [reg1+reg1]   ; 3 такта процессора
  48. shl reg1, 4
  49. sub reg1, reg2
  50.  
  51. ; Умножение на 15:
  52. lea reg1, [reg1+reg1*2] ; 4 такта процессора
  53. lea reg1, [reg1+reg1*4]
  54.  
  55. ; Умножение на 16:
  56. shl reg1, 4             ; 1 такт процессора
  57.  
  58. ; Умножение на 17:
  59. mov reg2, reg1          ; 2 такта процессора
  60. shl reg1, 4
  61. add reg1, reg2
  62.  
  63. ; Умножение на 18:
  64. lea reg1, [reg1+reg1*8] ; 3 такта процессора
  65. add reg1, reg1
  66.  
  67. ; Умножение на 19:
  68. lea reg2, [reg1+reg1*2] ; 3 такта процессора
  69. shl reg1, 4
  70. add reg1, reg2
  71.  
  72. ; Умножение на 20:
  73. lea reg1, [reg1+reg1*4] ; 3 такта процессора
  74. shl reg1, 2
  75.  
  76. ; Умножение на 21:
  77. lea reg2, [reg1+reg1*4] ; 3 такта процессора
  78. shl reg1, 4
  79. add reg1, reg2
  80.  
  81. ; Умножение на 22:
  82. lea reg2, [reg1+reg1*4] ; 4 такта процессора
  83. add reg1, reg1
  84. lea reg1, [reg1+reg2*4]
  85.  
  86. ; Умножение на 23:
  87. lea reg2, [reg1+reg1*8] ; 3 такта процессора
  88. shl reg1, 5
  89. sub reg1, reg2
  90.  
  91. ; Умножение на 24:
  92. lea reg1, [reg1+reg1*2] ; 3 такта процессора
  93. shl reg1, 3
  94.  
  95. ; Умножение на 25:
  96. lea reg1, [reg1+reg1*4] ; 4 такта процессора
  97. lea reg1, [reg1+reg1*4]
  98.  
  99. ; Умножение на 26:
  100. lea reg2, [reg1+reg1*2] ; 4 такта процессора
  101. add reg1, reg1
  102. lea reg1, [reg1+reg2*8]
  103.  
  104. ; Умножение на 27:
  105. lea reg1, [reg1+reg1*2] ; 4 такта процессора
  106. lea reg1, [reg1+reg1*8]
  107.  
  108. ; Умножение на 28:
  109. lea reg2, [reg1*4]      ; 3 такта процессора
  110. shl reg1, 5
  111. sub reg1, reg2
  112.  
  113. ; Умножение на 29:
  114. lea reg2, [reg1+reg1*2] ; 3 такта процессора
  115. shl reg1, 5
  116. sub reg1, reg2
  117.  
  118. ; Умножение на 30:
  119. lea reg2, [reg1+reg1]   ; 3 такта процессора
  120. shl reg1, 5
  121. sub reg1, reg2
  122.  
  123. ; Умножение на 31:
  124. mov reg2, reg1          ; 2 такта процессора
  125. shl reg1, 5
  126. sub reg1, reg2
  127.  
  128. ; Умножение на 32:
  129. shl reg1, 5             ; 1 такт процессора
В разделе мануала, посвященном оптимизации целочисленного умножения, упоминается программа "FINDMUL", которая находится на "AMD Documentation CD-ROM". Как написано в мануале, эта программа должна рассчитывать оптимальный алгоритм умножения на константу. К сожалению, ни саму программу, ни этот диск в интернетах мне найти не удалось, поэтому больше ничего про нее сказать не могу. Неизвестны ни алгоритм ее работы, ни диапазон констант, которые она в состоянии оптимизировать. Если кто-то знает где его взять, то, пожалуйста, напишите. Буду очень благодарен.

Читать статью целиком »
Просмотров: 8200 | Комментариев: 7

Замена деления умножением на Ассемблере

07.07.2013 | Категория: Образ мышления: Assembler | Автор: ManHunter

Замена деления умножением на Ассемблере

При написании на Ассемблере алгоритмов, использующих деление на константу, операции деления можно заменять на операции умножения. Зачем это надо? Дело в том, что процессор выполняет операцию умножения в несколько раз быстрее, чем операцию деления. К примеру, на умножение тратится 5-9 тактов процессора (знаковое умножение) или 4-8 (беззнаковое умножение), тогда как для деления необходимо 22-47 тактов (знаковое деление) или 17-41 тактов (беззнаковое деление). В высокооптимизированных алгоритмах, таких как шифрование, математические расчеты, архивация больших объемов данных, подсчет контрольных сумм и других подобных задачах, экономия каждого такта процессора может дать значительный прирост общей скорости работы программы.

В руководстве по оптимизации "AMD Athlon Processor x86 Code Optimization Guide" вопрос замены деления умножением расписан очень подробно. Рассмотрены частные случаи деления, такие как деление на степень двойки, описан выбор алгоритма для знакового и беззнакового деления и приведен код для вычисления вспомогательных корректирующих значений. Кроме этого, в руководстве описаны методы оптимизации и других математических операций. Даже если вы никогда не будете использовать на практике замену деления умножением, почитать о других способах оптимизации будет очень полезно.

Читать статью целиком »
Просмотров: 14449 | Комментариев: 4

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

31.05.2012 | Категория: Образ мышления: 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].

Читать статью целиком »
Просмотров: 8993 | Комментариев: 13

prev 01 02
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 3 (0.0032 сек.) / Память: 4.5 Mb
Наверх