Преобразование числа в строку с разделением на разряды
Преобразование числа в строку с разделением на разряды
Если вы работаете с большими десятичными числами, то наверняка согласитесь, что число с разделением на разряды (то есть с группировкой по три символа: тысячи, миллионы и так далее) воспринимается гораздо лучше, чем просто последовательность цифр. Так проще выявлять ошибки или, например, с одного взгляда можно оценить порядок числа.
Обычно для разделения строки на разряды рекомендуют использовать штатную функцию WinAPI GetNumberFormat. Действительно, с ней очень удобно работать: подали на вход число в виде строки без разбивки и получили на выходе его же, но уже отформатированное по любым заданным правилам. При необходимости эта функция даже умеет округлять дробные числа до нужного количества знаков, что тоже можно использовать при решении некоторых задач. За удобства приходится расплачиваться размером получаемого кода и необходимостью использовать дополнительные структуры и переменные.
Code (Assembler) : Убрать нумерацию
- ;--------------------------------------------------------------------
- ; Процедура преобразования числа в строку с разделением на разряды
- ;--------------------------------------------------------------------
- ; Параметры:
- ; lpBuff - указатель на строку, куда будет записан результат
- ; dNum - число для преобразования
- ;--------------------------------------------------------------------
- proc split_num lpBuff:DWORD, dNum:DWORD
- ; Структура для форматирования числа
- struct _NUMBERFMT
- NumDigits dd ?
- LeadingZero dd ?
- Grouping dd ?
- lpDecimalSep dd ?
- lpThousandSep dd ?
- NegativeOrder dd ?
- ends
- ; Локальные переменные
- locals
- tmp rb 16
- endl
- pusha
- ; Преобразовать число в строку
- lea ebx,[tmp]
- invoke wsprintf,ebx,.maski,[dNum]
- add esp,12
- ; Выделить память под структуру
- invoke GetProcessHeap
- mov esi,eax
- invoke HeapAlloc,esi,HEAP_ZERO_MEMORY,sizeof._NUMBERFMT
- ; Сохранить указатель для HeapFree
- push eax
- ; Отформатировать строку
- mov [eax+_NUMBERFMT.NumDigits],0
- mov [eax+_NUMBERFMT.Grouping],3
- mov [eax+_NUMBERFMT.lpDecimalSep],.szSep
- mov [eax+_NUMBERFMT.lpThousandSep],.szSep
- invoke GetNumberFormat,0,0,ebx,eax,[lpBuff],16
- ; Прибраться за собой
- invoke HeapFree,esi,0
- popa
- ret
- .maski db '%u',0
- .szSep db ',',0 ; Разделитель разрядов
- endp
Более компактный вариант получается, если делать разбивку строки самостоятельно. Тут не надо выделять никакую память, хватает одной локальной переменной. Но, конечно, про всякие автоматические округления и прочую красоту, доступную в GetNumberFormat, придется забыть, хотя для целых чисел это и не актуально.
Code (Assembler) : Убрать нумерацию
- ;--------------------------------------------------------------------
- ; Процедура преобразования числа в строку с разделением на разряды
- ;--------------------------------------------------------------------
- ; Параметры:
- ; lpBuff - указатель на строку, куда будет записан результат
- ; dNum - число для преобразования
- ;--------------------------------------------------------------------
- proc split_num lpBuff:DWORD, dNum:DWORD
- ; Локальные переменные
- locals
- tmp rb 16
- endl
- pusha
- lea esi,[tmp]
- ; Строка с разделением на разряды
- invoke wsprintf,esi,.maski,[dNum]
- add esp,12
- xor edx,edx
- mov ecx,3
- div ecx
- mov ecx,edx
- mov ebx,eax
- inc ebx
- mov edi,[lpBuff]
- cld
- .loc:
- rep movsb
- ; Разделитель разрядов
- or edx,edx
- jz @f
- mov al,','
- stosb
- @@:
- inc edx
- mov ecx,3
- dec ebx
- or ebx,ebx
- jnz .loc
- dec edi
- mov al,0
- stosb
- popa
- ret
- .maski db '%u',0
- endp
Code (Assembler) : Убрать нумерацию
- ;--------------------------------------------------------------------
- ; Процедура преобразования числа в строку с разделением на разряды
- ;--------------------------------------------------------------------
- ; Параметры:
- ; lpBuff - указатель на строку, куда будет записан результат
- ; dNum - число для преобразования
- ;--------------------------------------------------------------------
- proc split_num lpBuff:DWORD, dNum:DWORD
- pusha
- mov eax,[dNum]
- xor esi,esi
- xor ecx,ecx
- push ecx
- inc ecx
- .loc_loop:
- ; Получить следующую цифру
- xor edx,edx
- mov ebx,10
- div ebx
- add dl,'0'
- push edx
- inc ecx
- ; Все цифры сохранили?
- or eax,eax
- jz .loc_store
- ; Нужен разделитель разрядов?
- inc esi
- cmp esi,3
- jne @f
- mov dl,','
- push edx
- inc ecx
- xor esi,esi
- @@:
- jmp .loc_loop
- .loc_store:
- mov edi,[lpBuff]
- cld
- @@:
- pop eax
- stosb
- loop @b
- popa
- ret
- endp
Просмотров: 5263 | Комментариев: 9
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(18.01.2016 в 00:52):
Похоже на мой второй вариант.
user
(17.01.2016 в 20:01):
..мда. Ну, по теме топика:
Как делал раньше такое - конвертировал число в обычную строку,
а потом натравливал на эту строку процедуру, работающую чисто с текстом.
Вот, сконвертировал в FASM ту процедуру:
rghost.ru/6dTtXZsfQ
Как делал раньше такое - конвертировал число в обычную строку,
а потом натравливал на эту строку процедуру, работающую чисто с текстом.
Вот, сконвертировал в FASM ту процедуру:
rghost.ru/6dTtXZsfQ
brute
(17.01.2016 в 12:08):
вариант на PB:
s.s=Str(1234567874474354532)
l=Len(s)
i=2
While l>(i+1)
s = InsertString(s, "_|_", l-i)
i=i+3
Wend
MessageRequester("",s)
s.s=Str(1234567874474354532)
l=Len(s)
i=2
While l>(i+1)
s = InsertString(s, "_|_", l-i)
i=i+3
Wend
MessageRequester("",s)
insolor
(17.01.2016 в 06:06):
> Только вот по поводу кириллицы в комментариях - она в UTF-8 (cp855)
UTF-8 - это не cp855. Как-то даже совсем не рядом.
> Вооюще, все двухбайтовые кодировки необходимы для тех языков, в которых больше 80h символов
А еще для программ, поддерживающих несколько языков.
UTF-8 - это не cp855. Как-то даже совсем не рядом.
> Вооюще, все двухбайтовые кодировки необходимы для тех языков, в которых больше 80h символов
А еще для программ, поддерживающих несколько языков.
addhaloka
(17.01.2016 в 04:47):
APIW нормально реагируют, достаточно подключить UTF8.INC.
user
(16.01.2016 в 20:13):
Так в коииентариях может быть вообще любой набор символов - они все игнорируются ассемблером. вообще любой Аsm на такие вещи и не должен реанировать.
Впрочем, это и не юникод - настоящий юникод (2 байта на любой символ) FASM не обрабатывает. Ну, это в порядке оффтопа. Можно удалить..
Это не проблема.
Вооюще, все двухбайтовые кодировки необходимы для тех языков, в которых больше 80h символов, славянские сюда не относятся, - для них достаточно и DOS866 или WIN1251 кодировок. (Как и для инглиша, который в любой из них присутствует).
.. Только сразу вопрос, - а если набирать строковые переменные в кириллице UTF-8, то как на это будут реагировать APIA/APIW функции?
Всё равно ведь придётся их перед использованием конвертировать либо в ANSI, либо в юникод?
Впрочем, это и не юникод - настоящий юникод (2 байта на любой символ) FASM не обрабатывает. Ну, это в порядке оффтопа. Можно удалить..
Это не проблема.
Вооюще, все двухбайтовые кодировки необходимы для тех языков, в которых больше 80h символов, славянские сюда не относятся, - для них достаточно и DOS866 или WIN1251 кодировок. (Как и для инглиша, который в любой из них присутствует).
.. Только сразу вопрос, - а если набирать строковые переменные в кириллице UTF-8, то как на это будут реагировать APIA/APIW функции?
Всё равно ведь придётся их перед использованием конвертировать либо в ANSI, либо в юникод?
ManHunter
(16.01.2016 в 06:57):
Давно уже перешел на сборку всех FASM'овских проектов в Sublime Text, там никаких проблем с кодировкой нет. Компилятор тоже против юникода не возражает.
user
(16.01.2016 в 00:59):
Только вот по поводу кириллицы в комментариях - она в UTF-8 (cp855),
а дучше бы в ANSI (cp1251)
а дучше бы в ANSI (cp1251)
Добавить комментарий
Заполните форму для добавления комментария
На мой взгляд, самый предпочтительный вариант.
Оставляет простор для действий.
)) Сконвертировал в 32-bit FASM из старинной 16-bit TASM процедуры.
Специально слазил в архивы.