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

Тема в разделе "WASM.A&O", создана пользователем Y_Mur, 18 янв 2007.

  1. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Собственно вопрос всплывает регулярно, а вот дзенный ответ на него мне пока не попадался ;) Поэтому предлагаю этот ответ совместными усилиями слепить на радость Бегиннерсам :)
    И для разминки обогнать wsprintf раз этак в ~5 на PII(w98) и в ~4 на PM(XP) или Celeron 2,8ГГц(XP)
    Код (Text):
    1. ; --- Подпрограмма перевода целого числа в строку ---
    2. ; На входе: eax - число, edi - адрес строки
    3. ; На выходе: edi - ссылка на продолжение строки, ebx - размер записанного числа
    4. ; Для входа через метки WORD_2_Str и BYTE_2_Str требуется заранее скорректировать знак
    5. ; и установить соответственно ecx = 5 или ecx = 3
    6. IFDEF Var_2_Str_comp
    7.  SDWORD_2_Str PROC
    8.     .IF sdword ptr eax < 0
    9.         neg eax
    10.         mov byte ptr [edi], '-'
    11.         inc edi
    12.     .ENDIF
    13.  DWORD_2_Str = $    ; Вход для беззнакового dword
    14.   ; Отсев ведущих нулей
    15.     mov ecx, 10
    16.     cmp eax, 1000000000
    17.       jae @F
    18.       dec ecx
    19.     cmp eax, 100000000
    20.       jae @F
    21.       dec ecx
    22.     cmp eax, 10000000
    23.       jae @F
    24.       dec ecx
    25.     cmp eax, 1000000
    26.       jae @F
    27.       dec ecx
    28.     cmp eax, 100000
    29.       jae @F
    30.       dec ecx
    31.  WORD_2_Str = $     ; Вход для беззнакового word (нужен ecx = 5)
    32.     cmp eax, 10000
    33.       jae @F
    34.       dec ecx
    35.     cmp eax, 1000
    36.       jae @F
    37.       dec ecx
    38.  BYTE_2_Str = $     ; Вход для беззнакового byte (нужен ecx = 3)
    39.     cmp eax, 100
    40.       jae @F
    41.       dec ecx
    42.     cmp eax, 10
    43.       jae @F
    44.       dec ecx
    45.     @@:   ; Собственно преобразование
    46.     mov ebx, ecx    ; запомнить длину строки
    47.     add edi, ecx
    48.     inc ebx
    49.     dec edi
    50.     ;--------- Вариант с div (тормозной) ----------
    51. ;    mov esi, 10
    52. ;    @@:
    53. ;      xor edx, edx
    54. ;      div esi  ; edx:eax / 10
    55. ;      add edx, '0'
    56. ;      mov byte ptr [edi], dl
    57. ;      dec edi
    58. ;      dec ecx
    59. ;    jnz @B
    60.     ;---------- Вариант с магическим числом -------------
    61.     mov esi, eax    ; сохранить eax
    62.     @@:
    63.       mov edx, 3435973837 ; множитель для /10
    64.       mul edx
    65.       shr edx, 3   ; результат деления eax на 10
    66.       mov eax, esi ; восстановить eax
    67.       mov esi, edx ; запомнить результат деления
    68.       imul edx, 10
    69.       sub eax, edx ; остаток от деления
    70.       add eax, '0'
    71.       mov byte ptr [edi], al
    72.       dec edi    
    73.       mov eax, esi ; вернуть в eax результат деления для продолжения вычислений
    74.       dec ecx
    75.     jnz @B
    76.     ;-----------------------
    77.     add edi, ebx    ; перенести указатель на конец строки
    78.    ret
    79.  SDWORD_2_Str ENDP
    Но думаю это далеко не предел ;) Там и отсев нулей можно сбалансировать и наверняка ещё кто нибудь что нибудь заметит :)

    ЗЫ: а у wsprintf есть ещё "красявый" вариант использования (см. Test_Var_2_Str_wsprintf_tormoz.asm)
     
  2. PaCHER

    PaCHER New Member

    Публикаций:
    0
    Регистрация:
    25 мар 2006
    Сообщения:
    852
    http://wasm.ru/forum/viewtopic.php?id=15459
    БЫла уже темка, чтоб не переписовать глянь там есть 2 примера. Поменьше будет, да и поскоросте быстрее наверно.
     
  3. Freeman

    Freeman New Member

    Публикаций:
    0
    Регистрация:
    10 фев 2005
    Сообщения:
    1.385
    Адрес:
    Ukraine
    Код (Text):
    1. invoke wsprintf, edi, zSTR('wsprintf заняла   %u : %u    тактов'),\
    2.             dword ptr [v64_Tiks_2 + 04], addr colon, dword ptr [v64_Tiks_2 + 00]
    тут случаем ошибки нет????
     
  4. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    FreeManCPM
    О блин - спасибо, а я то удивляюсь откуда такая дикая разница в скорости :)
     
  5. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    PaCHER
    Да действитеьно побыстрее на PII в 1,5, а на Celerone в два раза :)
    Сенкс.
     
  6. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Y_Mur
    А если заглянуть в мануал по оптимизации AMD 64 ?
     
  7. Proteus

    Proteus Member

    Публикаций:
    0
    Регистрация:
    19 июн 2004
    Сообщения:
    344
    Адрес:
    Russia
    Правда тестировать как всегда не охота и по скорости не конкурент. Но в принципе и ваш вариант тоже кое какой дресировке поддаётся (могу его же в личку кинуть)...

    Код (Text):
    1.     mov eax,value
    2.     mov edi,@buffer
    3.     test    eax,eax
    4.     jns .1
    5.     neg eax
    6.     mov byte [edi],'-'
    7.     inc edi
    8. .1: push    -'0'
    9.     xor ecx,ecx
    10.     mov cl,10
    11. .2: cdq
    12.     div ecx
    13.     push    edx
    14.     test    eax,eax
    15.     jnz .2
    16. .3: pop eax
    17.     add al,'0'
    18.     stosb
    19.     jnz .3
    20.     ret
     
  8. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    leo как всегда прав - АМДэшный монстр удивительно шустр, вот только как его знаковым сделать, так чтобы не попортить эдакую безбранчевую красотищу условным переходом?
    Код (Text):
    1. [b]Binary-to-ASCII Decimal Conversion Suppressing Leading Zeros[/b]
    2. __declspec(naked) void __stdcall uint_to_ascii_nlz(char *sptr, unsigned int x)
    3. { __asm {
    4.   push edi      ; Save as per calling conventions.
    5.   push ebx      ; Save as per calling conventions.
    6.     mov edi, [esp+12]   ; sptr
    7.     mov eax, [esp+16]   ; x
    8.     mov ecx, eax    ; Save original argument.
    9.     mov edx, 89705F41h  ; 1e-9 * 2^61 rounded
    10.     mul edx     ; Divide by 1e9 by multiplying with reciprocal.
    11.     add eax, eax    ; Round division result.
    12.     adc edx, 0      ; EDX[31-29] = argument / 1e9
    13.     shr edx, 29     ; Leading decimal digit, 0...4
    14.     mov eax, edx    ; Leading digit
    15.     mov ebx, edx    ; Initialize digit accumulator with leading digit.
    16.     imul eax, 1000000000; Leading digit * 1e9
    17.     sub ecx, eax    ; Subtract (leading digit * 1e9) from argument.
    18.     or dl, '0'      ; Convert leading digit to ASCII.
    19.     mov [edi], dl   ; Store leading digit.
    20.     cmp ebx, 1      ; Any nonzero digit yet?
    21.     sbb edi, -1     ; Yes, increment ptr. No, keep old ptr.
    22.     mov eax, ecx    ; Get reduced argument < 1e9.
    23.     mov edx, 0abcc7712h ; 2^28 / 1e8 * 2^30 rounded up
    24.     mul edx     ; Divide reduced
    25.     shr eax, 30     ; argument < 1e9 by 1e8,
    26.     lea edx, [eax+4*edx+1]  ; converting it into 4.28 fixed-point
    27.     mov eax, edx    ; format such that 1.0 = 2^28.
    28.     shr eax, 28     ; Next digit
    29.     and edx, 0fffffffh  ; Fraction part
    30.     or ebx, eax     ; Accumulate next digit.
    31.     or eax, '0'     ; Convert digit to ASCII.
    32.     mov [edi], al   ; Store digit in memory.
    33.     lea eax, [edx*4+edx]    ; 5 * fraction, new digit EAX[31-27]
    34.     lea edx, [edx*4+edx]    ; 5 * fraction, new fraction EDX[26-0]
    35.     cmp ebx, 1  ; Any nonzero digit yet?
    36.     sbb edi, -1 ; Yes, increment ptr. No, keep old ptr.
    37.     shr eax, 27 ; Next digit
    38.     and edx, 07ffffffh  ; Fraction part
    39.     or ebx, eax ; Accumulate next digit.
    40.     or eax, '0' ; Convert digit to ASCII.
    41.     mov [edi], al   ; Store digit in memory.
    42.     lea eax, [edx*4+edx]    ; 5 * fraction, new digit EAX[31-26]
    43.     lea edx, [edx*4+edx]    ; 5 * fraction, new fraction EDX[25-0]
    44.     cmp ebx, 1  ; Any nonzero digit yet?
    45.     sbb edi, -1 ; Yes, increment ptr. No, keep old ptr.
    46.     shr eax, 26 ; Next digit
    47.     and edx, 03ffffffh  ; Fraction part
    48.     or ebx, eax ; Accumulate next digit.
    49.     or eax, '0' ; Convert digit to ASCII.
    50.     mov [edi], al   ; Store digit in memory.
    51.     lea eax, [edx*4+edx]    ; 5 * fraction, new digit EAX[31-25]
    52.     lea edx, [edx*4+edx]    ; 5 * fraction, new fraction EDX[24-0]
    53.     cmp ebx, 1  ; Any nonzero digit yet?
    54.     sbb edi, -1 ; Yes, increment ptr. No, keep old ptr.
    55.     shr eax, 25 ; Next digit
    56.     and edx, 01ffffffh  ; Fraction part
    57.     or ebx, eax ; Accumulate next digit.
    58.     or eax, '0' ; Convert digit to ASCII.
    59.     mov [edi], al   ; Store digit in memory.
    60.     lea eax, [edx*4+edx]    ; 5 * fraction, new digit EAX[31-24]
    61.     lea edx, [edx*4+edx]    ; 5 * fraction, new fraction EDX[23-0]
    62.     cmp ebx, 1  ; Any nonzero digit yet?
    63.     sbb edi, -1 ; Yes, increment ptr, No, keep old ptr.
    64.     shr eax, 24 ; Next digit
    65.     and edx, 00ffffffh  ; Fraction part
    66.     or ebx, eax ; Accumulate next digit.
    67.     or eax, '0' ; Convert digit to ASCII.
    68.     mov [edi], al   ; Store digit in memory.
    69.     lea eax, [edx*4+edx]    ; 5 * fraction, new digit EAX[31-23]
    70.     lea edx, [edx*4+edx]    ; 5 * fraction, new fraction EDX[31-23]
    71.     cmp ebx, 1  ; Any nonzero digit yet?
    72.     sbb edi, -1 ; Yes, increment ptr. No, keep old ptr.
    73.     shr eax, 23 ; Next digit
    74.     and edx, 007fffffh  ; Fraction part
    75.     or ebx, eax ; Accumulate next digit.
    76.     or eax, '0' ; Convert digit to ASCII.
    77.     mov [edi], al   ; Store digit out to memory.
    78.     lea eax, [edx*4+edx]    ; 5 * fraction, new digit EAX[31-22]
    79.     lea edx, [edx*4+edx]    ; 5 * fraction, new fraction EDX[22-0]
    80.     cmp ebx, 1  ; Any nonzero digit yet?
    81.     sbb edi, -1 ; Yes, increment ptr. No, keep old ptr.
    82.     shr eax, 22 ; Next digit
    83.     and edx, 003fffffh  ; Fraction part
    84.     OR ebx, eax ; Accumulate next digit.
    85.     or eax, '0' ; Convert digit to ASCII.
    86.     mov [edi], al   ; Store digit in memory.
    87.     lea eax, [edx*4+edx]    ; 5 * fraction, new digit EAX[31-21]
    88.     lea edx, [edx*4+edx]    ; 5 * fraction, new fraction EDX[21-0]
    89.     cmp ebx, 1  ; Any nonzero digit yet?
    90.     sbb edi, -1 ; Yes, increment ptr. No, keep old ptr.
    91.     shr eax, 21 ; Next digit
    92.     and edx, 001fffffh  ; Fraction part
    93.     or ebx, eax ; Accumulate next digit.
    94.     or eax, '0' ; Convert digit to ASCII.
    95.     mov [edi], al   ; Store digit in memory.
    96.     lea eax, [edx*4+edx]    ; 5 * fraction, new digit EAX[31-20]
    97.     cmp ebx, 1  ; Any nonzero digit yet?
    98.     sbb edi, -1 ; Yes, increment ptr. No, keep old ptr.
    99.     shr eax, 20 ; Next digit
    100.     or eax, '0' ; Convert digit to ASCII.
    101.     mov [edi], ax   ; Store last digit and end marker in memory.
    102.   pop ebx   ; Restore register as per calling convention.
    103.   pop edi   ; Restore register as per calling convention.
    104. ret 8   ; Pop two DWORD arguments and return.
    105.  }
    106. }
     
  9. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Извини, разбираться в этом "монструозном" коде как-то лень, тем более я считаю, что сверхбыстродействие для itoa\IntToStr не нужно, т.к. обычно это преобразование выполняется для вывода результатов на экран или в файл, что само по себе не быстро ;)
    А безбранчевый учет знака можно сделать так:
    Код (Text):
    1. ;eax - число, edi - указатель на строку
    2. ;1) стандартный безбранчевый abs
    3. cdq             ;if eax >= 0 then edx=0 else edx=-1
    4. xor eax,edx
    5. sub eax,edx
    6. ;2)  пишем '-' в строку, но передвигаем edi только при eax < 0
    7. mov byte [edi],'-'
    8. sub edi,edx   ;if eax < 0 then {inc edi} else {затрем '-' первой цифрой}
     
  10. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    leo
    Спасибо, симпатичное решение :)
     
  11. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Ну и для полноты картины вывод 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 байт

    Младшая цифра результата везде округляется
     
  12. Tim Sobolev

    Tim Sobolev New Member

    Публикаций:
    0
    Регистрация:
    23 мар 2005
    Сообщения:
    53
    Y_Mur

    Спасибо!

    в сырцах МАСМа:

    Код (Text):
    1. FPTOA.ASM
    2. FPTOA2.ASM
    я нашел решение своего вопроса. А форматирование добавить не проблема. Буду переписывать под С и заганять в либу.