Author Topic: fast signed dword to ascii conversion  (Read 22146 times)

Offline brethren

  • Jr. Member
  • *
  • Posts: 28
fast signed dword to ascii conversion
« on: April 04, 2011, 08:40:31 PM »
i've just been converting some of my procedures from masm syntax and though i'd post this:) last time i timed this routine is was about 8 times faster than the comparable C function

ps i've updated this code on 13th April 2011, due to a slight bug:) its now been tested across the full 32-bit range
Code: [Select]
;-----------------------------------------------------------------
;very fast signed dword to ascii string conversion
;input: EAX=number to convert, EBX=buffer to hold the string
;returns: EAX points to the ascii string which contains the number
;-----------------------------------------------------------------
GLOBAL dwtoa
SECTION .text
dwtoa:
       
        push ebx                        ;this push is popped into EAX for the return value
        push esi
        push ecx
        push edx

        mov esi, 0CCCCCCCDh             ;magic number that eliminates the need for div by 10 (see agner fogs optimisation tips)

        test eax, eax                   ;test if the number is negative or positive
        jns .CalcNumOfDigits            ;jump forward if number is positive

        mov byte[ebx], '-'              ;number is negative, add '-' to the start of the string
        inc ebx                         ;and increment the string pointer
        neg eax                         ;get the absolute value of the negative number, ready for the conversion

.CalcNumOfDigits:
       
        ;optimisation that eliminates the need for string reversal
        cmp eax, 9                      ;does the number have 1 digit?
        ja .lbl2                        ;jump if the number has more than 1 digit
        inc ebx                         ;else make space for 1 digit in the string
        jmp .TerminateStr               ;and then add the null terminator to the string
.lbl2:
        cmp eax, 99                     ;does the string have 2 digits?
        ja .lbl3                        ;jump if the number has more than 2 digits
        add ebx, 2                      ;else make space for 2 digits in the string
        jmp .TerminateStr               ;and then add the null terminator
.lbl3:
        cmp eax, 999                    ;does the string have 3 digits?
        ja .lbl4
        add ebx, 3
        jmp .TerminateStr
.lbl4:
        cmp eax, 9999                   ;4
        ja .lbl5
        add ebx, 4
        jmp .TerminateStr
.lbl5:
        cmp eax, 99999                  ;5
        ja .lbl6
        add ebx, 5
        jmp .TerminateStr
.lbl6:
        cmp eax, 999999                 ;6
        ja .lbl7
        add ebx, 6
        jmp .TerminateStr
.lbl7:
        cmp eax, 9999999                ;7
        ja .lbl8
        add ebx, 7
        jmp .TerminateStr
.lbl8:
        cmp eax, 99999999               ;8
        ja .lbl9
        add ebx, 8
        jmp .TerminateStr
.lbl9:
        cmp eax, 999999999              ;9   
        ja .lbl10
        add ebx, 9
        jmp .TerminateStr
.lbl10:
        add ebx, 10                     ;10

.TerminateStr:
        mov byte[ebx], 0                ;add null terminator to the string
        dec ebx                         ;ebx now points to the place where the first number will be inserted into the string

.DoConversion:
        mov ecx, eax                    ;store eax's value as we'll need this to calculate the remainder

        ;the following two instructions divide eax by 10 and store the result in edx (see agner fogs optimisation tips)
        mul esi
        shr edx, 3

        mov eax, edx                    ;store the result of the division back in eax ready for the next division

        ;the following three instructions multiply edx by 10, subtract edx from the original value to get the remainder (which
        ;is the digit we need) then adds '0' to convert the digit to ascii
        lea edx, [edx+edx*4]
        lea edx, [edx+edx-'0']
        sub ecx, edx

        mov [ebx], cl                   ;place the ascii digit in the string
        sub ebx, 1                      ;adjust string pointer, ready for the next character to be inserted
        test eax, eax                   ;if eax=0 then we have finished the conversion
        jz .Finished

        ;loop unrolled twice for extra speed
        mov ecx, eax
        mul esi
        shr edx, 3
        mov eax, edx
        lea edx, [edx+edx*4]
        lea edx, [edx+edx-'0']
        sub ecx, edx
        mov [ebx], cl
        sub ebx, 1
        test eax, eax
        jnz .DoConversion

.Finished:
        pop edx
        pop ecx
        pop esi
        pop eax
        ret
« Last Edit: April 13, 2011, 06:50:16 PM by brethren »

Offline JoeCoder

  • Jr. Member
  • *
  • Posts: 72
  • Country: by
Re: fast signed dword to ascii conversion
« Reply #1 on: June 24, 2011, 06:13:45 AM »
Thanks, I will look at that. I am going to need to write something to do that soon and I will try to learn from what you did.
If you can't code it in assembly, it can't be coded!