Собственно вопрос всплывает регулярно, а вот дзенный ответ на него мне пока не попадался Поэтому предлагаю этот ответ совместными усилиями слепить на радость Бегиннерсам И для разминки обогнать wsprintf раз этак в ~5 на PII(w98) и в ~4 на PM(XP) или Celeron 2,8ГГц(XP) Код (Text): ; --- Подпрограмма перевода целого числа в строку --- ; На входе: eax - число, edi - адрес строки ; На выходе: edi - ссылка на продолжение строки, ebx - размер записанного числа ; Для входа через метки WORD_2_Str и BYTE_2_Str требуется заранее скорректировать знак ; и установить соответственно ecx = 5 или ecx = 3 IFDEF Var_2_Str_comp SDWORD_2_Str PROC .IF sdword ptr eax < 0 neg eax mov byte ptr [edi], '-' inc edi .ENDIF DWORD_2_Str = $ ; Вход для беззнакового dword ; Отсев ведущих нулей mov ecx, 10 cmp eax, 1000000000 jae @F dec ecx cmp eax, 100000000 jae @F dec ecx cmp eax, 10000000 jae @F dec ecx cmp eax, 1000000 jae @F dec ecx cmp eax, 100000 jae @F dec ecx WORD_2_Str = $ ; Вход для беззнакового word (нужен ecx = 5) cmp eax, 10000 jae @F dec ecx cmp eax, 1000 jae @F dec ecx BYTE_2_Str = $ ; Вход для беззнакового byte (нужен ecx = 3) cmp eax, 100 jae @F dec ecx cmp eax, 10 jae @F dec ecx @@: ; Собственно преобразование mov ebx, ecx ; запомнить длину строки add edi, ecx inc ebx dec edi ;--------- Вариант с div (тормозной) ---------- ; mov esi, 10 ; @@: ; xor edx, edx ; div esi ; edx:eax / 10 ; add edx, '0' ; mov byte ptr [edi], dl ; dec edi ; dec ecx ; jnz @B ;---------- Вариант с магическим числом ------------- mov esi, eax ; сохранить eax @@: mov edx, 3435973837 ; множитель для /10 mul edx shr edx, 3 ; результат деления eax на 10 mov eax, esi ; восстановить eax mov esi, edx ; запомнить результат деления imul edx, 10 sub eax, edx ; остаток от деления add eax, '0' mov byte ptr [edi], al dec edi mov eax, esi ; вернуть в eax результат деления для продолжения вычислений dec ecx jnz @B ;----------------------- add edi, ebx ; перенести указатель на конец строки ret SDWORD_2_Str ENDP Но думаю это далеко не предел Там и отсев нулей можно сбалансировать и наверняка ещё кто нибудь что нибудь заметит ЗЫ: а у wsprintf есть ещё "красявый" вариант использования (см. Test_Var_2_Str_wsprintf_tormoz.asm)
http://wasm.ru/forum/viewtopic.php?id=15459 БЫла уже темка, чтоб не переписовать глянь там есть 2 примера. Поменьше будет, да и поскоросте быстрее наверно.
Код (Text): invoke wsprintf, edi, zSTR('wsprintf заняла %u : %u тактов'),\ dword ptr [v64_Tiks_2 + 04], addr colon, dword ptr [v64_Tiks_2 + 00] тут случаем ошибки нет????
Правда тестировать как всегда не охота и по скорости не конкурент. Но в принципе и ваш вариант тоже кое какой дресировке поддаётся (могу его же в личку кинуть)... Код (Text): mov eax,value mov edi,@buffer test eax,eax jns .1 neg eax mov byte [edi],'-' inc edi .1: push -'0' xor ecx,ecx mov cl,10 .2: cdq div ecx push edx test eax,eax jnz .2 .3: pop eax add al,'0' stosb jnz .3 ret
leo как всегда прав - АМДэшный монстр удивительно шустр, вот только как его знаковым сделать, так чтобы не попортить эдакую безбранчевую красотищу условным переходом? Код (Text): [b]Binary-to-ASCII Decimal Conversion Suppressing Leading Zeros[/b] __declspec(naked) void __stdcall uint_to_ascii_nlz(char *sptr, unsigned int x) { __asm { push edi ; Save as per calling conventions. push ebx ; Save as per calling conventions. mov edi, [esp+12] ; sptr mov eax, [esp+16] ; x mov ecx, eax ; Save original argument. mov edx, 89705F41h ; 1e-9 * 2^61 rounded mul edx ; Divide by 1e9 by multiplying with reciprocal. add eax, eax ; Round division result. adc edx, 0 ; EDX[31-29] = argument / 1e9 shr edx, 29 ; Leading decimal digit, 0...4 mov eax, edx ; Leading digit mov ebx, edx ; Initialize digit accumulator with leading digit. imul eax, 1000000000; Leading digit * 1e9 sub ecx, eax ; Subtract (leading digit * 1e9) from argument. or dl, '0' ; Convert leading digit to ASCII. mov [edi], dl ; Store leading digit. cmp ebx, 1 ; Any nonzero digit yet? sbb edi, -1 ; Yes, increment ptr. No, keep old ptr. mov eax, ecx ; Get reduced argument < 1e9. mov edx, 0abcc7712h ; 2^28 / 1e8 * 2^30 rounded up mul edx ; Divide reduced shr eax, 30 ; argument < 1e9 by 1e8, lea edx, [eax+4*edx+1] ; converting it into 4.28 fixed-point mov eax, edx ; format such that 1.0 = 2^28. shr eax, 28 ; Next digit and edx, 0fffffffh ; Fraction part or ebx, eax ; Accumulate next digit. or eax, '0' ; Convert digit to ASCII. mov [edi], al ; Store digit in memory. lea eax, [edx*4+edx] ; 5 * fraction, new digit EAX[31-27] lea edx, [edx*4+edx] ; 5 * fraction, new fraction EDX[26-0] cmp ebx, 1 ; Any nonzero digit yet? sbb edi, -1 ; Yes, increment ptr. No, keep old ptr. shr eax, 27 ; Next digit and edx, 07ffffffh ; Fraction part or ebx, eax ; Accumulate next digit. or eax, '0' ; Convert digit to ASCII. mov [edi], al ; Store digit in memory. lea eax, [edx*4+edx] ; 5 * fraction, new digit EAX[31-26] lea edx, [edx*4+edx] ; 5 * fraction, new fraction EDX[25-0] cmp ebx, 1 ; Any nonzero digit yet? sbb edi, -1 ; Yes, increment ptr. No, keep old ptr. shr eax, 26 ; Next digit and edx, 03ffffffh ; Fraction part or ebx, eax ; Accumulate next digit. or eax, '0' ; Convert digit to ASCII. mov [edi], al ; Store digit in memory. lea eax, [edx*4+edx] ; 5 * fraction, new digit EAX[31-25] lea edx, [edx*4+edx] ; 5 * fraction, new fraction EDX[24-0] cmp ebx, 1 ; Any nonzero digit yet? sbb edi, -1 ; Yes, increment ptr. No, keep old ptr. shr eax, 25 ; Next digit and edx, 01ffffffh ; Fraction part or ebx, eax ; Accumulate next digit. or eax, '0' ; Convert digit to ASCII. mov [edi], al ; Store digit in memory. lea eax, [edx*4+edx] ; 5 * fraction, new digit EAX[31-24] lea edx, [edx*4+edx] ; 5 * fraction, new fraction EDX[23-0] cmp ebx, 1 ; Any nonzero digit yet? sbb edi, -1 ; Yes, increment ptr, No, keep old ptr. shr eax, 24 ; Next digit and edx, 00ffffffh ; Fraction part or ebx, eax ; Accumulate next digit. or eax, '0' ; Convert digit to ASCII. mov [edi], al ; Store digit in memory. lea eax, [edx*4+edx] ; 5 * fraction, new digit EAX[31-23] lea edx, [edx*4+edx] ; 5 * fraction, new fraction EDX[31-23] cmp ebx, 1 ; Any nonzero digit yet? sbb edi, -1 ; Yes, increment ptr. No, keep old ptr. shr eax, 23 ; Next digit and edx, 007fffffh ; Fraction part or ebx, eax ; Accumulate next digit. or eax, '0' ; Convert digit to ASCII. mov [edi], al ; Store digit out to memory. lea eax, [edx*4+edx] ; 5 * fraction, new digit EAX[31-22] lea edx, [edx*4+edx] ; 5 * fraction, new fraction EDX[22-0] cmp ebx, 1 ; Any nonzero digit yet? sbb edi, -1 ; Yes, increment ptr. No, keep old ptr. shr eax, 22 ; Next digit and edx, 003fffffh ; Fraction part OR ebx, eax ; Accumulate next digit. or eax, '0' ; Convert digit to ASCII. mov [edi], al ; Store digit in memory. lea eax, [edx*4+edx] ; 5 * fraction, new digit EAX[31-21] lea edx, [edx*4+edx] ; 5 * fraction, new fraction EDX[21-0] cmp ebx, 1 ; Any nonzero digit yet? sbb edi, -1 ; Yes, increment ptr. No, keep old ptr. shr eax, 21 ; Next digit and edx, 001fffffh ; Fraction part or ebx, eax ; Accumulate next digit. or eax, '0' ; Convert digit to ASCII. mov [edi], al ; Store digit in memory. lea eax, [edx*4+edx] ; 5 * fraction, new digit EAX[31-20] cmp ebx, 1 ; Any nonzero digit yet? sbb edi, -1 ; Yes, increment ptr. No, keep old ptr. shr eax, 20 ; Next digit or eax, '0' ; Convert digit to ASCII. mov [edi], ax ; Store last digit and end marker in memory. pop ebx ; Restore register as per calling convention. pop edi ; Restore register as per calling convention. ret 8 ; Pop two DWORD arguments and return. } }
Извини, разбираться в этом "монструозном" коде как-то лень, тем более я считаю, что сверхбыстродействие для itoa\IntToStr не нужно, т.к. обычно это преобразование выполняется для вывода результатов на экран или в файл, что само по себе не быстро А безбранчевый учет знака можно сделать так: Код (Text): ;eax - число, edi - указатель на строку ;1) стандартный безбранчевый abs cdq ;if eax >= 0 then edx=0 else edx=-1 xor eax,edx sub eax,edx ;2) пишем '-' в строку, но передвигаем edi только при eax < 0 mov byte [edi],'-' sub edi,edx ;if eax < 0 then {inc edi} else {затрем '-' первой цифрой}
Ну и для полноты картины вывод FPU значений стандартными методами: include \masm32\include\msvcrt.inc includelib \masm32\lib\msvcrt.lib ... invoke crt__gcvt, [ConvNumber], ndec, addr szResult где: [ConvNumber] - FPU qword ndec - требуемое количество цифр (без учёта символов '-' и '.') szResult - буфер размером не менее 21 байт gcvt сама выбирает как представлять данные "обычно" или экспонинциально ... invoke crt__ecvt, [ConvNumber], ndec, addr decpt, addr sign invoke crt__fcvt, [ConvNumber], ndec, addr decpt, addr sign где возвращаемые значения: eax - адрес статического буфера с результатом (выделяется msvcrt.dll) decpt - позиция запятой, sign - знак = 0 если число положительно или 1 если отрицательно Обе функции не добавляют в строку ни знак ни запятую, предоставляя это вызывающему Отличие функций: ecvt - ndec = задаёт общее количество символов, fcvt - ndec = задаёт количество символов после запятой (ведущие нули отсекаются даже в числе типа 0.0001). И конечно масмовская FloatToStr, которая не позволяет задавать количество знаков (разве только через исправление входящих в комплект масм исходников), однако на фоне crt весьма шустрая: include \masm32\include\masm32.inc includelib \masm32\lib\masm32.lib ... invoke FloatToStr, [ConvNumber], addr szResult szResult - здесь буфер размером не менее 19 байт Младшая цифра результата везде округляется
Y_Mur Спасибо! в сырцах МАСМа: Код (Text): FPTOA.ASM FPTOA2.ASM я нашел решение своего вопроса. А форматирование добавить не проблема. Буду переписывать под С и заганять в либу.