Быстрый поиск
Введите фрагмент названия статьи для поиска
Преобразование числа в строку с разделением на разряды
17.01.2016 | Категория: Образ мышления: Assembler | Автор: ManHunter
Преобразование числа в строку с разделением на разряды
Если вы работаете с большими десятичными числами, то наверняка согласитесь, что число с разделением на разряды (то есть с группировкой по три символа: тысячи, миллионы и так далее) воспринимается гораздо лучше, чем просто последовательность цифр. Так проще выявлять ошибки или, например, с одного взгляда можно оценить порядок числа.
Читать статью целиком »
Просмотров: 5375 | Комментариев: 9
Преобразование вещественного числа в строку на Ассемблере
14.03.2014 | Категория: Образ мышления: Assembler | Автор: ManHunter
Ранее я выкладывал статью о том, как на Ассемблере перевести строку в вещественное число, теперь настало время произвести обратную операцию, то есть перевести вещественное число в строку. В отличие от целых чисел, где достаточно нескольких строк кода, с преобразованием вещественных чисел пришлось повозиться. В результате появилась следующая функция. За основу взята аналогичная функция из пакета MASM, но со значительными доработками.Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------
- ; Функция перевода вещественного числа в строку
- ;------------------------------------------------------------
- ; Портирование с MASM, доработка и оптимизация - ManHunter
- ; Параметры:
- ; lpFloat - указатель на вещественное число TBYTE
- ; lpResult - указатель на строку-приемник результата
- ;------------------------------------------------------------
- proc FloatToString lpFloat:DWORD, lpResult:DWORD
- ; Локальные переменные
- local digits_count:DWORD
- local tmp:DWORD
- local old_cw:WORD
- local new_cw:WORD
- local saved_float:TBYTE
- local tmp1 rb 12h
- local tmp2 rb 12h
- ; Сохранить все регистры
- pusha
- ; Указатель на строку-приемник
- mov edi,[lpResult]
- ; Это ноль?
- mov esi,[lpFloat]
- cmp dword [esi],0
- jne loc_not_zero
- cmp dword [esi+4],0
- jne loc_not_zero
- cmp word [esi+8],0x8000
- je loc_minus_zero
- cmp word [esi+8],0
- jne loc_not_zero
- ; Записать в строку ноль
- mov al,'0'
- stosb
- jmp loc_ret
- loc_minus_zero:
- ; Записать в строку минус ноль
- mov ax,'-0'
- stosw
- jmp loc_ret
- loc_not_zero:
- ; Denormalized?
- mov ax,word [esi+8]
- and ax,7F80h
- or ax,ax
- jnz loc_not_denorm
- cmp dword [esi],0
- jne loc_denorm
- cmp dword [esi+4],0
- je loc_not_denorm
- loc_denorm:
- mov esi,szDen
- movsd
- movsd
- movsd
- jmp loc_ret
- loc_not_denorm:
- ; Infinity или NaN?
- mov ax,word [esi+8]
- and ax,7F80h
- cmp ax,7F80h
- jne loc_not_inf_nan
- ; Это Infinity?
- cmp dword [esi],0
- jne loc_nan
- cmp dword [esi+4],80000000h
- jne loc_nan
- loc_infinity:
- mov esi,szInf
- movsd
- movsb
- loc_plus_minus:
- movsd
- mov esi,[lpFloat]
- test byte [esi+9],80h
- jz loc_ret
- mov esi,[lpResult]
- mov byte[esi],'-'
- jmp loc_ret
- loc_nan:
- mov esi,szNan
- jmp loc_plus_minus
- loc_not_inf_nan:
- ; Скопировать число в локальную переменную
- push edi
- mov esi,[lpFloat]
- lea edi,[saved_float]
- movsd
- movsd
- movsw
- pop edi
- ; Число отрицательное?
- cmp dword [saved_float+6],0
- jge loc_not_signed
- ; Привести число к абсолютному значению
- and byte [saved_float+9],7Fh
- ; Записать в строку минус
- mov al,'-'
- stosb
- loc_not_signed:
- ; Проверить число на наличие дробной части и
- ; подсчитать количество цифр в нем
- fclex
- ; Сохранить управляющее слово
- fstcw [old_cw]
- ; Установить управляющее слово
- mov [new_cw],0000001001111111b
- fldcw [new_cw]
- lea esi,[saved_float]
- fld tbyte [esi]
- fld st
- ; Выделить мантиссу и порядок
- fxtract
- fstp st
- fldlg2
- ; Получить количество цифр в числе
- fmulp st1,st
- fistp [digits_count]
- ; Если цифр больше 16, то число отображается в
- ; нормализованном виде с мантиссой и экспонентой
- cmp [digits_count],10h
- jnb loc_not_integer
- ; У числа есть дробная часть?
- fld st
- frndint
- fcomp st1
- fstsw ax
- test ah,01000000b
- ; Да, отображать число с дробной частью
- jz loc_not_integer
- ; Целое число без дробной части и экспоненты
- lea eax,[tmp1]
- fbstp [eax]
- ; Перевести BCD-число в строку
- push edi
- lea esi,[tmp1+8]
- lea edi,[tmp2]
- mov ecx, 9
- @@:
- std
- xor eax,eax
- lodsb
- cld
- rol ax,12
- rol ah,4
- add ax,'00'
- stosw
- loop @b
- pop edi
- ; Пропустить лидирующий ноль
- mov eax,11h
- mov ecx,[digits_count]
- sub eax,ecx
- inc ecx
- lea esi,[tmp2+eax]
- cmp byte [esi],'0'
- jne @f
- inc esi
- dec ecx
- @@:
- ; Перенести полученное число из временного буфера
- rep movsb
- jmp loc_clear_stack
- loc_not_integer:
- mov eax,10h
- sub eax,[digits_count]
- ; Преобразовать число в целое до 16 разрядов
- mov ecx,eax
- cmp eax,0
- jge @f
- neg eax
- @@:
- ; Для чисел больше 0 корректировка округления в сторону 0
- mov [new_cw],0000101001111111b
- cmp ecx,0
- jge @f
- mov [new_cw],0000011001111111b
- @@:
- ; Установить управляющее слово
- fldcw [new_cw]
- ; Возвести 10 в степень количества цифр
- mov [tmp],eax
- fild [tmp]
- fld [float2]
- fyl2x
- fld1
- fld st1
- fprem
- f2xm1
- faddp
- fscale
- ; Почистить стек
- fxch st1
- fstp st
- ; Если число меньше 0, то умножить, иначе разделить
- cmp ecx,0
- jge @f
- fdivp st1,st
- jmp loc_rounded
- @@:
- fmulp st1,st
- loc_rounded:
- ; Полученное значение меньше 1.0e16 ?
- fcom [float1]
- fstsw ax
- test ah,1
- jz @f
- fmul [float2]
- dec [digits_count]
- @@:
- ; Целое число без дробной части и экспоненты
- lea eax,[tmp1]
- fbstp [eax]
- ; Перевести BCD-число в строку
- push edi
- lea esi,[tmp1+8]
- lea edi,[tmp2]
- mov ecx, 9
- @@:
- std
- xor eax,eax
- lodsb
- cld
- rol ax,12
- rol ah,4
- add ax,'00'
- stosw
- loop @b
- pop edi
- ; Числу требуется мантисса и экспонента?
- lea esi,[tmp2+1]
- mov ecx,[digits_count]
- cmp ecx,-0Fh
- jl loc_mantiss_and_exponent
- cmp ecx,10h
- jg loc_mantiss_and_exponent
- ; Заполнить дробную часть числа
- inc ecx
- cmp ecx,0
- jg @f
- mov ax,'0.'
- stosw
- neg ecx
- mov al,'0'
- rep stosb
- mov ecx,10h
- jmp loc_fraction_filled
- @@:
- rep movsb
- mov al,'.'
- stosb
- mov ecx,10h
- sub ecx,[digits_count]
- loc_fraction_filled:
- rep movsb
- jmp @f
- loc_clear_fraction:
- ; Удалить завершающие нули дробной части
- dec edi
- @@:
- cmp byte [edi-1],'0'
- jz loc_clear_fraction
- cmp byte [edi-1],'.'
- jnz @f
- dec edi
- @@:
- jmp loc_clear_stack
- loc_mantiss_and_exponent:
- ; Дробная часть мантиссы
- movsb
- mov al,'.'
- stosb
- movsd
- movsd
- movsw
- ; Удалить завершающие нули дробной части
- @@:
- cmp byte [edi-1],'0'
- jne @f
- cmp byte [edi-2],'.'
- je @f
- dec edi
- jmp @b
- @@:
- ; Символ и знак экспоненты
- mov al,'e'
- stosb
- mov al,'+'
- mov ebx,[digits_count]
- cmp ebx, 0
- jge @f
- mov al,'-'
- neg ebx
- @@:
- stosb
- ; Значение экспоненты
- mov eax,ebx
- mov ecx,10
- mov ebx,4
- @@:
- dec ebx
- xor edx,edx
- div ecx
- add dl,'0'
- mov [tmp1+ebx],dl
- or ebx,ebx
- jnz @b
- ; Пропустить лидирующие нули экспоненты
- mov ecx,4
- lea esi,[tmp1]
- @@:
- lodsb
- cmp al,'0'
- jne @f
- dec ecx
- jmp @b
- @@:
- dec esi
- rep movsb
- loc_clear_stack:
- ; Восстановить управляющее слово
- fldcw [old_cw]
- loc_ret:
- ; Окончание строки
- mov al,0
- stosb
- ; Восстановить все регистры
- popa
- ret
- float1 dq 1.0e16
- float2 dq 10.0
- szInf db '+Infinity'
- szNan db '+NaN'
- szDen db 'Denormalized'
- endp
Читать статью целиком »
Просмотров: 11572 | Комментариев: 27
Оптимизация умножения на константу на Ассемблере
05.08.2013 | Категория: Образ мышления: Assembler | Автор: ManHunter
В предыдущей статье по оптимизации мы рассматривали замену команд деления, как это описано в "AMD Athlon Processor x86 Code Optimization Guide". Но в этом же мануале описана оптимизация умножения на константу. Тут нет универсальных алгоритмов, по которым можно было бы однозначно определить нужную последовательность команд замены, как в случае с делением. Для каждой константы набор команд определяется на основании их "весовых коэффициентов" и количества тактов процессора, необходимых для их выполнения. В качестве примера приведена вот такая табличка оптимизации умножения с 2 до 32. Ее всегда можно посмотреть и в самом мануале, но лучше пусть будет под рукой.Code (Assembler) : Убрать нумерацию
- ; Умножение на 2
- add reg1, reg1 ; 1 такт процессора
- ; Умножение на 3
- lea reg1, [reg1+reg1*2] ; 2 такта процессора
- ; Умножение на 4
- shl reg1, 2 ; 1 такт процессора
- ; Умножение на 5
- lea reg1, [reg1+reg1*4] ; 2 такта процессора
- ; Умножение на 6
- lea reg1, [reg1+reg1*2] ; 3 такта процессора
- add reg1, reg1
- ; Умножение на 7:
- mov reg2, reg1 ; 2 такта процессора
- shl reg1, 3
- sub reg1, reg2
- ; Умножение на 8:
- shl reg1, 3 ; 1 такт процессора
- ; Умножение на 9:
- lea reg1, [reg1+reg1*8] ; 2 такта процессора
- ; Умножение на 10:
- lea reg1, [reg1+reg1*4] ; 3 такта процессора
- add reg1, reg1
- ; Умножение на 11:
- lea reg2, [reg1+reg1*8] ; 3 такта процессора
- add reg1, reg1
- add reg1, reg2
- ; Умножение на 12:
- lea reg1, [reg1+reg1*2] ; 3 такта процессора
- shl reg1, 2
- ; Умножение на 13:
- lea reg2, [reg1+reg1*2] ; 3 такта процессора
- shl reg1, 4
- sub reg1, reg2
- ; Умножение на 14:
- lea reg2, [reg1+reg1] ; 3 такта процессора
- shl reg1, 4
- sub reg1, reg2
- ; Умножение на 15:
- lea reg1, [reg1+reg1*2] ; 4 такта процессора
- lea reg1, [reg1+reg1*4]
- ; Умножение на 16:
- shl reg1, 4 ; 1 такт процессора
- ; Умножение на 17:
- mov reg2, reg1 ; 2 такта процессора
- shl reg1, 4
- add reg1, reg2
- ; Умножение на 18:
- lea reg1, [reg1+reg1*8] ; 3 такта процессора
- add reg1, reg1
- ; Умножение на 19:
- lea reg2, [reg1+reg1*2] ; 3 такта процессора
- shl reg1, 4
- add reg1, reg2
- ; Умножение на 20:
- lea reg1, [reg1+reg1*4] ; 3 такта процессора
- shl reg1, 2
- ; Умножение на 21:
- lea reg2, [reg1+reg1*4] ; 3 такта процессора
- shl reg1, 4
- add reg1, reg2
- ; Умножение на 22:
- lea reg2, [reg1+reg1*4] ; 4 такта процессора
- add reg1, reg1
- lea reg1, [reg1+reg2*4]
- ; Умножение на 23:
- lea reg2, [reg1+reg1*8] ; 3 такта процессора
- shl reg1, 5
- sub reg1, reg2
- ; Умножение на 24:
- lea reg1, [reg1+reg1*2] ; 3 такта процессора
- shl reg1, 3
- ; Умножение на 25:
- lea reg1, [reg1+reg1*4] ; 4 такта процессора
- lea reg1, [reg1+reg1*4]
- ; Умножение на 26:
- lea reg2, [reg1+reg1*2] ; 4 такта процессора
- add reg1, reg1
- lea reg1, [reg1+reg2*8]
- ; Умножение на 27:
- lea reg1, [reg1+reg1*2] ; 4 такта процессора
- lea reg1, [reg1+reg1*8]
- ; Умножение на 28:
- lea reg2, [reg1*4] ; 3 такта процессора
- shl reg1, 5
- sub reg1, reg2
- ; Умножение на 29:
- lea reg2, [reg1+reg1*2] ; 3 такта процессора
- shl reg1, 5
- sub reg1, reg2
- ; Умножение на 30:
- lea reg2, [reg1+reg1] ; 3 такта процессора
- shl reg1, 5
- sub reg1, reg2
- ; Умножение на 31:
- mov reg2, reg1 ; 2 такта процессора
- shl reg1, 5
- sub reg1, reg2
- ; Умножение на 32:
- shl reg1, 5 ; 1 такт процессора
Читать статью целиком »
Просмотров: 8395 | Комментариев: 7
Замена деления умножением на Ассемблере
07.07.2013 | Категория: Образ мышления: Assembler | Автор: ManHunter
Замена деления умножением на Ассемблере
При написании на Ассемблере алгоритмов, использующих деление на константу, операции деления можно заменять на операции умножения. Зачем это надо? Дело в том, что процессор выполняет операцию умножения в несколько раз быстрее, чем операцию деления. К примеру, на умножение тратится 5-9 тактов процессора (знаковое умножение) или 4-8 (беззнаковое умножение), тогда как для деления необходимо 22-47 тактов (знаковое деление) или 17-41 тактов (беззнаковое деление). В высокооптимизированных алгоритмах, таких как шифрование, математические расчеты, архивация больших объемов данных, подсчет контрольных сумм и других подобных задачах, экономия каждого такта процессора может дать значительный прирост общей скорости работы программы.
В руководстве по оптимизации "AMD Athlon Processor x86 Code Optimization Guide" вопрос замены деления умножением расписан очень подробно. Рассмотрены частные случаи деления, такие как деление на степень двойки, описан выбор алгоритма для знакового и беззнакового деления и приведен код для вычисления вспомогательных корректирующих значений. Кроме этого, в руководстве описаны методы оптимизации и других математических операций. Даже если вы никогда не будете использовать на практике замену деления умножением, почитать о других способах оптимизации будет очень полезно.
Читать статью целиком »
Просмотров: 14668 | Комментариев: 4
Преобразование строки в вещественное число на Ассемблере
31.05.2012 | Категория: Образ мышления: Assembler | Автор: ManHunter
Про преобразование строки в обычное число я уже писал, теперь задача посложнее - надо преобразовать строку в вещественное число с плавающей запятой (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
Читать статью целиком »
Просмотров: 9247 | Комментариев: 13