The MASM Forum

General => The Laboratory => Topic started by: hutch-- on March 15, 2015, 12:37:15 AM

Title: Integer to ascii algorithm.
Post by: hutch-- on March 15, 2015, 12:37:15 AM
This algo was originally designed by Paul Dixon in PowerBASIC, I have converted it to MASM notation and removed the stack frame and it is testing up fine. It will handle both signed and unsigned 32 bit integers and long ago I did exhaustive testing on the algo and it works correctly over the full range. I have in mind eventually replacing the dwtoa() used in the str$() macro with this algo. It is probably the fastest I have seen that does a conventional integer to ascii conversion writing to a pre-allocated buffer.


; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
    include \masm32\include\masm32rt.inc
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

comment * -----------------------------------------------------
                        Build this  template with
                       "CONSOLE ASSEMBLE AND LINK"
        ----------------------------------------------------- *

    ntoa$ MACRO variable
      LOCAL buffer
      .data?
        align 16
        buffer db 32 dup (?)
      .code
      push OFFSET buffer
      push variable
      call numtostr
      mov eax, OFFSET buffer
      EXITM <eax>
    ENDM

 ;     ShowESP MACRO
 ;       IFNDEF esp_reg
 ;         .data
 ;           esp_reg dd (0)
 ;         .code
 ;       ENDIF
 ;       mov esp_reg, esp
 ;       print ustr$(esp_reg)," = ESP stack address",13,10
 ;     ENDM

    ShowESP MACRO
      IFNDEF esp_reg
        .data?
          esp_reg dd ?
        .code
      ENDIF
      mov esp_reg, esp
      print ustr$(esp_reg)," = ESP stack address",13,10
    ENDM

    numtostr PROTO LongVar:DWORD,answer:DWORD

    .code

start:
   
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    call main
    inkey
    exit

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

main proc

    LOCAL pnum[16]:DWORD

    mov pnum[0], 10293847
    mov pnum[4], -44444444

    lea esi, pnum

    ShowESP

    mov eax, 87654321
    print ntoa$(eax),13,10

    ShowESP

    print ntoa$([esi]),13,10
    print ntoa$([esi+4]),13,10
    print ntoa$(-12345678),13,10
    print ntoa$(-1),13,10
    print ntoa$(0),13,10

    ret

main endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE

numtostr proc LongVar:DWORD,answer:DWORD

  ; -----------------------------------------
  ; This algorithm was designed by Paul Dixon
  ; -----------------------------------------

.data
align 4
chartab \
    dd "00","10","20","30","40","50","60","70","80","90"
    dd "01","11","21","31","41","51","61","71","81","91"
    dd "02","12","22","32","42","52","62","72","82","92"
    dd "03","13","23","33","43","53","63","73","83","93"
    dd "04","14","24","34","44","54","64","74","84","94"
    dd "05","15","25","35","45","55","65","75","85","95"
    dd "06","16","26","36","46","56","66","76","86","96"
    dd "07","17","27","37","47","57","67","77","87","97"
    dd "08","18","28","38","48","58","68","78","88","98"
    dd "09","19","29","39","49","59","69","79","89","99"
.code

    push esi
    push edi

    mov eax, [esp+4][8]         ; LongVar   get number
    mov ecx, [esp+8][8]         ; answer    get pointer to answer string
 
  ; --------------------------
  ; do a signed DWORD to ASCII
  ; --------------------------
    or eax,eax                  ; test sign
    jns udword                  ; if +ve, continue as for unsigned
    neg eax                     ; else, make number positive
    mov byte ptr [ecx],"-"      ; include the - sign
    inc ecx                     ; update the pointer

udword:
  ; -----------------------
  ; unsigned DWORD to ASCII
  ; -----------------------
    mov esi,ecx                 ; get pointer to answer
    mov edi,eax                 ; save a copy of the number
 
    mov edx, 0D1B71759h         ; =2^45\10000    13 bit extra shift
    mul edx                     ; gives 6 high digits in edx
 
    mov eax, 68DB9h             ; =2^32\10000+1

    shr edx,13                  ; correct for multiplier offset used to give better accuracy
    jz skiphighdigits           ; if zero then don't need to process the top 6 digits

    mov ecx,edx                 ; get a copy of high digits
    imul ecx,10000              ; scale up high digits
    sub edi,ecx                 ; subtract high digits from original. EDI now = lower 4 digits

    mul edx                     ; get first 2 digits in edx
    mov ecx,100                 ; load ready for later

    jnc next1                   ; if zero, supress them by ignoring
    cmp edx,9                   ; 1 digit or 2?
    ja  ZeroSupressed           ; 2 digits, just continue with pairs of digits to the end

    mov edx,chartab[edx*4]      ; look up 2 digits
    mov [esi],dh                ; but only write the 1 we need, supress the leading zero
    inc esi                     ; update pointer by 1
    jmp ZS1                     ; continue with pairs of digits to the end

next1:
    mul ecx                     ; get next 2 digits
    jnc next2                   ; if zero, supress them by ignoring
    cmp edx,9                   ; 1 digit or 2?
    ja  ZS1a                    ; 2 digits, just continue with pairs of digits to the end

    mov edx,chartab[edx*4]      ; look up 2 digits
    mov [esi],dh                ; but only write the 1 we need, supress the leading zero
    inc esi                     ; update pointer by 1
    jmp ZS2                     ; continue with pairs of digits to the end

next2:
    mul ecx                     ; get next 2 digits
    jnc next3                   ; if zero, supress them by ignoring
    cmp edx,9                   ; 1 digit or 2?
    ja  ZS2a                    ; 2 digits, just continue with pairs of digits to the end

    mov edx,chartab[edx*4]      ; look up 2 digits
    mov [esi],dh                ; but only write the 1 we need, supress the leading zero
    inc esi                     ; update pointer by 1
    jmp ZS3                     ; continue with pairs of digits to the end

next3:
 
skiphighdigits:
    mov eax,edi                 ; get lower 4 digits
    mov ecx,100
    mov edx, 28F5C29h           ; 2^32\100 +1
    mul edx
    jnc next4                   ; if zero, supress them by ignoring
    cmp edx,9                   ; 1 digit or 2?
    ja  ZS3a                    ; 2 digits, just continue with pairs of digits to the end

    mov edx,chartab[edx*4]      ; look up 2 digits
    mov [esi],dh                ; but only write the 1 we need, supress the leading zero
    inc esi                     ; update pointer by 1
    jmp  ZS4                    ; continue with pairs of digits to the end

next4:
    mul ecx                     ; this is the last pair so don't supress a single zero
    cmp edx,9                   ; 1 digit or 2?
    ja  ZS4a                    ; 2 digits, just continue with pairs of digits to the end

    mov edx,chartab[edx*4]      ; look up 2 digits
    mov [esi],dh                ; but only write the 1 we need, supress the leading zero
    mov byte ptr [esi+1],0      ; zero terminate string

    jmp  xit                    ; all done

ZeroSupressed:
    mov edx,chartab[edx*4]      ; look up 2 digits
    mov [esi],dx
    add esi,2                   ; write them to answer

ZS1:
    mul ecx                     ; get next 2 digits
ZS1a:
    mov edx,chartab[edx*4]      ; look up 2 digits
    mov [esi],dx                ; write them to answer
    add esi,2

ZS2:
    mul ecx                     ; get next 2 digits
ZS2a:
    mov edx,chartab[edx*4]      ; look up 2 digits
    mov [esi],dx                ; write them to answer
    add esi,2

ZS3:
    mov eax,edi                 ; get lower 4 digits
    mov edx,28F5C29h            ; 2^32\100 +1
    mul edx                     ; edx= top pair
ZS3a:
    mov edx,chartab[edx*4]      ; look up 2 digits
    mov [esi],dx                ; write to answer
    add esi,2                   ; update pointer

ZS4:
    mul ecx                     ; get final 2 digits
ZS4a:
    mov edx,chartab[edx*4]      ; look them up
    mov [esi],dx                ; write to answer

    mov byte ptr [esi+2],0      ; zero terminate string

xit:
sdwordend:
    pop edi
    pop esi

    ret 8

numtostr endp

OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

end start
Title: Re: Integer to ascii algorithm.
Post by: dedndave on March 15, 2015, 02:10:15 AM
signed or unsigned ?   :redface:
Title: Re: Integer to ascii algorithm.
Post by: Vortex on March 15, 2015, 05:45:38 AM
Hi Dave,

It will handle both signed and unsigned 32 bit integers
Title: Re: Integer to ascii algorithm.
Post by: dedndave on March 15, 2015, 06:13:00 AM
lol
well, yah - unsigned if below 2147483648
that is what we call "signed integers"
Title: Re: Integer to ascii algorithm.
Post by: Antariy on March 15, 2015, 06:20:33 AM
signed or unsigned ?   :redface:

Dave, do you see it as signed probably?

Erol, this code is not conditional, so, the algo is signed
Code: [Select]
  ; --------------------------
  ; do a signed DWORD to ASCII
  ; --------------------------
    or eax,eax                  ; test sign
    jns udword                  ; if +ve, continue as for unsigned
    neg eax                     ; else, make number positive
    mov byte ptr [ecx],"-"      ; include the - sign
    inc ecx                     ; update the pointer

It's probably better to add the third param to the algo: the isSigned boolean flag. And some code to check the flag and the signedness of the number at the same step.

Code: [Select]
numtostr proc LongVar:DWORD,answer:DWORD, isSigned:DWORD

    push esi
    push edi

    mov eax, [esp+4][8]         ; LongVar   get number
    mov ecx, [esp+8][8]         ; answer    get pointer to answer string

mov edx,[esp+4][8]
shl edx,1
mov edx,2
sbb edx,[esp+12][8] ; if signed and bool is 1 then edx is zero, no jump
                    ; if not signed and bool is 1 then edx is not zero, jump over conversion to unsigned
                    ; if bool is 0 then either of #31 bit set of the number - do not convert


jnz udword                  ; if +ve OR the unsigned mode continue as for unsigned

    neg eax                     ; else, make number positive
    mov byte ptr [ecx],"-"      ; include the - sign
    inc ecx                     ; update the pointer

udword:

the added/changed code is without indent.
Title: Re: Integer to ascii algorithm.
Post by: dedndave on March 15, 2015, 07:01:11 AM
It will handle both signed and unsigned 32 bit integers...

i think it's a "signed routine"
Title: Re: Integer to ascii algorithm.
Post by: Antariy on March 15, 2015, 07:11:32 AM
It will handle both signed and unsigned 32 bit integers...

i think it's a "signed routine"

My question was merely rhetorical, Dave :t
Title: Re: Integer to ascii algorithm.
Post by: dedndave on March 15, 2015, 07:14:10 AM
my point was this...

Hutch said it will handle both
it will handle signed values

there is no way to tell it you want anything else
Title: Re: Integer to ascii algorithm.
Post by: Antariy on March 15, 2015, 07:19:49 AM
my point was this...

Hutch said it will handle both
it will handle signed values

there is no way to tell it you want anything else

I did understand you, Dave :biggrin: And you may notice that am saying the same about signedness of algo and the need to add a flag to make algo (un)signed.
Title: Re: Integer to ascii algorithm.
Post by: dedndave on March 15, 2015, 07:34:24 AM
that's correct   :t

range of signed dword integers: -2147483648 to +2147483647
range of unsigned dword integers: 0 to 4294967295

for my ling long kai fang routines, i provide 3 versions:
1) signed
2) unsigned
3) signed or unsigned
with the last one, there is an added argument to tell it whether you want signed or unsigned
Title: Re: Integer to ascii algorithm.
Post by: hutch-- on March 15, 2015, 08:24:16 AM
Yes sad to say, I only did preliminary testing and did not modify the algo and the results are signed. I will have a play because I think there is a simple way to switch from signed to unsigned which can be tested cheaply.

If you block comment the following it produces unsigned. The other option which is what I originally preferred is to have one of each, signed and unsigned.


 ;   ; --------------------------
 ;   ; do a signed DWORD to ASCII
 ;   ; --------------------------
 ;     or eax,eax                  ; test sign
 ;     jns udword                  ; if +ve, continue as for unsigned
 ;     neg eax                     ; else, make number positive
 ;     mov byte ptr [ecx],"-"      ; include the - sign
 ;     inc ecx                     ; update the pointer
Title: Re: Integer to ascii algorithm.
Post by: hutch-- on March 15, 2015, 08:46:08 AM
Here is a quick play to do both algos separately. Results look like this.

1638212 = ESP stack address
87654321 +unsigned+
1638212 = ESP stack address
10293847 +unsigned+
-44444444 -signed-
-12345678 -signed-
-1 -signed-
4294967295 +unsigned+
0 +unsigned+
Press any key to continue ...

The test piece.

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
    include \masm32\include\masm32rt.inc
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

comment * -----------------------------------------------------
                        Build this  template with
                       "CONSOLE ASSEMBLE AND LINK"
        ----------------------------------------------------- *

    utoa$ MACRO variable
      LOCAL buffer
      .data?
        align 16
        buffer db 32 dup (?)
      .code
      push OFFSET buffer
      push variable
      call unumtostr
      mov eax, OFFSET buffer
      EXITM <eax>
    ENDM

    stoa$ MACRO variable
      LOCAL buffer
      .data?
        align 16
        buffer db 32 dup (?)
      .code
      push OFFSET buffer
      push variable
      call snumtostr
      mov eax, OFFSET buffer
      EXITM <eax>
    ENDM

    ShowESP MACRO
      IFNDEF esp_reg
        .data?
          esp_reg dd ?
        .code
      ENDIF
      mov esp_reg, esp
      print ustr$(esp_reg)," = ESP stack address",13,10
    ENDM

    unumtostr PROTO LongVar:DWORD,answer:DWORD
    snumtostr PROTO LongVar:DWORD,answer:DWORD

    .code

start:
   
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    call main
    inkey
    exit

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

main proc

    LOCAL pnum[16]:DWORD

    mov pnum[0], 10293847
    mov pnum[4], -44444444

    lea esi, pnum

    ShowESP

    mov eax, 87654321
    print utoa$(eax)," +unsigned+",13,10

    ShowESP

    print utoa$([esi])," +unsigned+",13,10
    print stoa$([esi+4])," -signed-",13,10
    print stoa$(-12345678)," -signed-",13,10
    print stoa$(-1)," -signed-",13,10
    print utoa$(4294967295)," +unsigned+",13,10
    print utoa$(0)," +unsigned+",13,10

    ret

main endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE

unumtostr proc LongVar:DWORD,answer:DWORD

  ; -----------------------------------------
  ; This algorithm was designed by Paul Dixon
  ; -----------------------------------------

.data
align 4
chartab \
    dd "00","10","20","30","40","50","60","70","80","90"
    dd "01","11","21","31","41","51","61","71","81","91"
    dd "02","12","22","32","42","52","62","72","82","92"
    dd "03","13","23","33","43","53","63","73","83","93"
    dd "04","14","24","34","44","54","64","74","84","94"
    dd "05","15","25","35","45","55","65","75","85","95"
    dd "06","16","26","36","46","56","66","76","86","96"
    dd "07","17","27","37","47","57","67","77","87","97"
    dd "08","18","28","38","48","58","68","78","88","98"
    dd "09","19","29","39","49","59","69","79","89","99"
.code

    push esi
    push edi

    mov eax, [esp+4][8]         ; LongVar   get number
    mov ecx, [esp+8][8]         ; answer    get pointer to answer string
 
 ;   ; --------------------------
 ;   ; do a signed DWORD to ASCII
 ;   ; --------------------------
 ;     or eax,eax                  ; test sign
 ;     jns udword                  ; if +ve, continue as for unsigned
 ;     neg eax                     ; else, make number positive
 ;     mov byte ptr [ecx],"-"      ; include the - sign
 ;     inc ecx                     ; update the pointer

udword:
  ; -----------------------
  ; unsigned DWORD to ASCII
  ; -----------------------
    mov esi,ecx                 ; get pointer to answer
    mov edi,eax                 ; save a copy of the number
 
    mov edx, 0D1B71759h         ; =2^45\10000    13 bit extra shift
    mul edx                     ; gives 6 high digits in edx
 
    mov eax, 68DB9h             ; =2^32\10000+1

    shr edx,13                  ; correct for multiplier offset used to give better accuracy
    jz skiphighdigits           ; if zero then don't need to process the top 6 digits

    mov ecx,edx                 ; get a copy of high digits
    imul ecx,10000              ; scale up high digits
    sub edi,ecx                 ; subtract high digits from original. EDI now = lower 4 digits

    mul edx                     ; get first 2 digits in edx
    mov ecx,100                 ; load ready for later

    jnc next1                   ; if zero, supress them by ignoring
    cmp edx,9                   ; 1 digit or 2?
    ja  ZeroSupressed           ; 2 digits, just continue with pairs of digits to the end

    mov edx,chartab[edx*4]      ; look up 2 digits
    mov [esi],dh                ; but only write the 1 we need, supress the leading zero
    inc esi                     ; update pointer by 1
    jmp ZS1                     ; continue with pairs of digits to the end

next1:
    mul ecx                     ; get next 2 digits
    jnc next2                   ; if zero, supress them by ignoring
    cmp edx,9                   ; 1 digit or 2?
    ja  ZS1a                    ; 2 digits, just continue with pairs of digits to the end

    mov edx,chartab[edx*4]      ; look up 2 digits
    mov [esi],dh                ; but only write the 1 we need, supress the leading zero
    inc esi                     ; update pointer by 1
    jmp ZS2                     ; continue with pairs of digits to the end

next2:
    mul ecx                     ; get next 2 digits
    jnc next3                   ; if zero, supress them by ignoring
    cmp edx,9                   ; 1 digit or 2?
    ja  ZS2a                    ; 2 digits, just continue with pairs of digits to the end

    mov edx,chartab[edx*4]      ; look up 2 digits
    mov [esi],dh                ; but only write the 1 we need, supress the leading zero
    inc esi                     ; update pointer by 1
    jmp ZS3                     ; continue with pairs of digits to the end

next3:
 
skiphighdigits:
    mov eax,edi                 ; get lower 4 digits
    mov ecx,100
    mov edx, 28F5C29h           ; 2^32\100 +1
    mul edx
    jnc next4                   ; if zero, supress them by ignoring
    cmp edx,9                   ; 1 digit or 2?
    ja  ZS3a                    ; 2 digits, just continue with pairs of digits to the end

    mov edx,chartab[edx*4]      ; look up 2 digits
    mov [esi],dh                ; but only write the 1 we need, supress the leading zero
    inc esi                     ; update pointer by 1
    jmp  ZS4                    ; continue with pairs of digits to the end

next4:
    mul ecx                     ; this is the last pair so don't supress a single zero
    cmp edx,9                   ; 1 digit or 2?
    ja  ZS4a                    ; 2 digits, just continue with pairs of digits to the end

    mov edx,chartab[edx*4]      ; look up 2 digits
    mov [esi],dh                ; but only write the 1 we need, supress the leading zero
    mov byte ptr [esi+1],0      ; zero terminate string

    jmp  xit                    ; all done

ZeroSupressed:
    mov edx,chartab[edx*4]      ; look up 2 digits
    mov [esi],dx
    add esi,2                   ; write them to answer

ZS1:
    mul ecx                     ; get next 2 digits
ZS1a:
    mov edx,chartab[edx*4]      ; look up 2 digits
    mov [esi],dx                ; write them to answer
    add esi,2

ZS2:
    mul ecx                     ; get next 2 digits
ZS2a:
    mov edx,chartab[edx*4]      ; look up 2 digits
    mov [esi],dx                ; write them to answer
    add esi,2

ZS3:
    mov eax,edi                 ; get lower 4 digits
    mov edx,28F5C29h            ; 2^32\100 +1
    mul edx                     ; edx= top pair
ZS3a:
    mov edx,chartab[edx*4]      ; look up 2 digits
    mov [esi],dx                ; write to answer
    add esi,2                   ; update pointer

ZS4:
    mul ecx                     ; get final 2 digits
ZS4a:
    mov edx,chartab[edx*4]      ; look them up
    mov [esi],dx                ; write to answer

    mov byte ptr [esi+2],0      ; zero terminate string

xit:
sdwordend:
    pop edi
    pop esi

    ret 8

unumtostr endp

OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE

snumtostr proc LongVar:DWORD,answer:DWORD

  ; -----------------------------------------
  ; This algorithm was designed by Paul Dixon
  ; -----------------------------------------

.data
align 4
schartab \
    dd "00","10","20","30","40","50","60","70","80","90"
    dd "01","11","21","31","41","51","61","71","81","91"
    dd "02","12","22","32","42","52","62","72","82","92"
    dd "03","13","23","33","43","53","63","73","83","93"
    dd "04","14","24","34","44","54","64","74","84","94"
    dd "05","15","25","35","45","55","65","75","85","95"
    dd "06","16","26","36","46","56","66","76","86","96"
    dd "07","17","27","37","47","57","67","77","87","97"
    dd "08","18","28","38","48","58","68","78","88","98"
    dd "09","19","29","39","49","59","69","79","89","99"
.code

    push esi
    push edi

    mov eax, [esp+4][8]         ; LongVar   get number
    mov ecx, [esp+8][8]         ; answer    get pointer to answer string
 
  ; --------------------------
  ; do a signed DWORD to ASCII
  ; --------------------------
    or eax,eax                  ; test sign
    jns udword                  ; if +ve, continue as for unsigned
    neg eax                     ; else, make number positive
    mov byte ptr [ecx],"-"      ; include the - sign
    inc ecx                     ; update the pointer

udword:
  ; -----------------------
  ; unsigned DWORD to ASCII
  ; -----------------------
    mov esi,ecx                 ; get pointer to answer
    mov edi,eax                 ; save a copy of the number
 
    mov edx, 0D1B71759h         ; =2^45\10000    13 bit extra shift
    mul edx                     ; gives 6 high digits in edx
 
    mov eax, 68DB9h             ; =2^32\10000+1

    shr edx,13                  ; correct for multiplier offset used to give better accuracy
    jz skiphighdigits           ; if zero then don't need to process the top 6 digits

    mov ecx,edx                 ; get a copy of high digits
    imul ecx,10000              ; scale up high digits
    sub edi,ecx                 ; subtract high digits from original. EDI now = lower 4 digits

    mul edx                     ; get first 2 digits in edx
    mov ecx,100                 ; load ready for later

    jnc next1                   ; if zero, supress them by ignoring
    cmp edx,9                   ; 1 digit or 2?
    ja  ZeroSupressed           ; 2 digits, just continue with pairs of digits to the end

    mov edx,schartab[edx*4]      ; look up 2 digits
    mov [esi],dh                ; but only write the 1 we need, supress the leading zero
    inc esi                     ; update pointer by 1
    jmp ZS1                     ; continue with pairs of digits to the end

next1:
    mul ecx                     ; get next 2 digits
    jnc next2                   ; if zero, supress them by ignoring
    cmp edx,9                   ; 1 digit or 2?
    ja  ZS1a                    ; 2 digits, just continue with pairs of digits to the end

    mov edx,schartab[edx*4]      ; look up 2 digits
    mov [esi],dh                ; but only write the 1 we need, supress the leading zero
    inc esi                     ; update pointer by 1
    jmp ZS2                     ; continue with pairs of digits to the end

next2:
    mul ecx                     ; get next 2 digits
    jnc next3                   ; if zero, supress them by ignoring
    cmp edx,9                   ; 1 digit or 2?
    ja  ZS2a                    ; 2 digits, just continue with pairs of digits to the end

    mov edx,schartab[edx*4]      ; look up 2 digits
    mov [esi],dh                ; but only write the 1 we need, supress the leading zero
    inc esi                     ; update pointer by 1
    jmp ZS3                     ; continue with pairs of digits to the end

next3:
 
skiphighdigits:
    mov eax,edi                 ; get lower 4 digits
    mov ecx,100
    mov edx, 28F5C29h           ; 2^32\100 +1
    mul edx
    jnc next4                   ; if zero, supress them by ignoring
    cmp edx,9                   ; 1 digit or 2?
    ja  ZS3a                    ; 2 digits, just continue with pairs of digits to the end

    mov edx,schartab[edx*4]      ; look up 2 digits
    mov [esi],dh                ; but only write the 1 we need, supress the leading zero
    inc esi                     ; update pointer by 1
    jmp  ZS4                    ; continue with pairs of digits to the end

next4:
    mul ecx                     ; this is the last pair so don't supress a single zero
    cmp edx,9                   ; 1 digit or 2?
    ja  ZS4a                    ; 2 digits, just continue with pairs of digits to the end

    mov edx,schartab[edx*4]      ; look up 2 digits
    mov [esi],dh                ; but only write the 1 we need, supress the leading zero
    mov byte ptr [esi+1],0      ; zero terminate string

    jmp  xit                    ; all done

ZeroSupressed:
    mov edx,schartab[edx*4]      ; look up 2 digits
    mov [esi],dx
    add esi,2                   ; write them to answer

ZS1:
    mul ecx                     ; get next 2 digits
ZS1a:
    mov edx,schartab[edx*4]      ; look up 2 digits
    mov [esi],dx                ; write them to answer
    add esi,2

ZS2:
    mul ecx                     ; get next 2 digits
ZS2a:
    mov edx,schartab[edx*4]      ; look up 2 digits
    mov [esi],dx                ; write them to answer
    add esi,2

ZS3:
    mov eax,edi                 ; get lower 4 digits
    mov edx,28F5C29h            ; 2^32\100 +1
    mul edx                     ; edx= top pair
ZS3a:
    mov edx,schartab[edx*4]      ; look up 2 digits
    mov [esi],dx                ; write to answer
    add esi,2                   ; update pointer

ZS4:
    mul ecx                     ; get final 2 digits
ZS4a:
    mov edx,schartab[edx*4]      ; look them up
    mov [esi],dx                ; write to answer

    mov byte ptr [esi+2],0      ; zero terminate string

xit:
sdwordend:
    pop edi
    pop esi

    ret 8

snumtostr endp

OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

end start
Title: Re: Integer to ascii algorithm.
Post by: dedndave on March 15, 2015, 10:02:58 AM
The other option which is what I originally preferred is to have one of each, signed and unsigned.

if you're looking for speed, that's probably the best option
but - you could make the table global and put it in a module of its' own   :t
Title: Re: Integer to ascii algorithm.
Post by: nidud on March 16, 2015, 02:23:00 AM
I tested a simple DIV routine to convert:
Code: [Select]
xor edx,edx
div ecx

The DIV instruction is very slow but simple.
It's possible to do this manually for each x10:
Code: [Select]
xor edx,edx
cmp eax,ecx
jb quit
mov esi,1 ; add zero after this..
@@:
inc edx
sub eax,ecx
cmp eax,ecx
jnb @B
quit:
add edx,'0'
mov [edi],dl
add edi,esi

using CALL for each:
Code: [Select]
mov ecx,1000000000
call add_x10
mov ecx,100000000
call add_x10
mov ecx,10000000
call add_x10
mov ecx,1000000
call add_x10
mov ecx,100000
call add_x10
mov ecx,10000
call add_x10
mov ecx,1000
call add_x10
mov ecx,100
call add_x10
mov ecx,10
call add_x10

inline as macro using Jxx:
Code: [Select]
add10 1000000000
add10 100000000
add10 10000000
add10 1000000
add10 100000
add10 10000
add10 1000
add10 100
add10 10

a lot of code to replace a simple DIV instruction

Code: [Select]
AMD Athlon(tm) II X2 245 Processor (SSE3)
----------------------------------------------
--------- 0
1587714   cycles - (  0) proc_0: crt_sprinf : 0
157528   cycles - (  0) proc_1: numtostr   : 0
120088   cycles - (327) proc_2: itoa Jxx   : 0
255028   cycles - ( 66) proc_3: itoa DIV   : 0
172615   cycles - (186) proc_4: itoa CALL  : 0
--------- -123
1660592   cycles - (  0) proc_0: crt_sprinf : -123
110642   cycles - (  0) proc_1: numtostr   : -123
98970   cycles - (327) proc_2: itoa Jxx   : -123
372774   cycles - ( 66) proc_3: itoa DIV   : -123
138022   cycles - (186) proc_4: itoa CALL  : -123
--------- 1234
1638572   cycles - (  0) proc_0: crt_sprinf : 1234
85646   cycles - (  0) proc_1: numtostr   : 1234
114376   cycles - (327) proc_2: itoa Jxx   : 1234
409165   cycles - ( 66) proc_3: itoa DIV   : 1234
193818   cycles - (186) proc_4: itoa CALL  : 1234
--------- -12345
1622600   cycles - (  0) proc_0: crt_sprinf : -12345
102374   cycles - (  0) proc_1: numtostr   : -12345
111704   cycles - (327) proc_2: itoa Jxx   : -12345
434155   cycles - ( 66) proc_3: itoa DIV   : -12345
186019   cycles - (186) proc_4: itoa CALL  : -12345
--------- 123456
1591438   cycles - (  0) proc_0: crt_sprinf : 123456
89714   cycles - (  0) proc_1: numtostr   : 123456
151219   cycles - (327) proc_2: itoa Jxx   : 123456
464446   cycles - ( 66) proc_3: itoa DIV   : 123456
233774   cycles - (186) proc_4: itoa CALL  : 123456
--------- -1234567
1661275   cycles - (  0) proc_0: crt_sprinf : -1234567
84012   cycles - (  0) proc_1: numtostr   : -1234567
165394   cycles - (327) proc_2: itoa Jxx   : -1234567
511458   cycles - ( 66) proc_3: itoa DIV   : -1234567
244820   cycles - (186) proc_4: itoa CALL  : -1234567
--------- 12345678
1698134   cycles - (  0) proc_0: crt_sprinf : 12345678
67211   cycles - (  0) proc_1: numtostr   : 12345678
166512   cycles - (327) proc_2: itoa Jxx   : 12345678
529416   cycles - ( 66) proc_3: itoa DIV   : 12345678
291925   cycles - (186) proc_4: itoa CALL  : 12345678


result:
   697127 cycles - (660) proc_1: numtostr
   928263 cycles - (327) proc_2: itoa Jxx
  1460993 cycles - (186) proc_4: itoa CALL
  2976442 cycles - ( 66) proc_3: itoa DIV
 11460325 cycles - (  0) proc_0: crt_sprinf
Title: Re: Integer to ascii algorithm.
Post by: LarryC on March 16, 2015, 05:21:24 AM
The original signed algorithm was between 20 and 40 cycles for a 1-10 digit conversion including the routine overhead.
Title: Re: Integer to ascii algorithm.
Post by: jj2007 on March 19, 2015, 08:33:11 AM
Just in case you are running a GUI app, the combination of SetDlgItemInt and WM_GETTEXT is an attractive original WinAPI option 8)
Title: Re: Integer to ascii algorithm.
Post by: Mikl__ on July 09, 2015, 05:26:07 PM
Convertion of 64 bit unsigned integer to decimal ASCIIZ string (http://www.madwizard.org/programming/snippets?id=25)
Code: [Select]
.586
.MMX
.model flat,stdcall
option casemap:none
.data
ALIGN 8
mmxb0F dq 0F0F0F0F0F0F0F0Fh
mmxb30 dq 3030303030303030h
num dq 12345678901234567890
.data?
buffer db 24 dup (?)
.code

    mov edx,dword ptr num+4
    mov eax,dword ptr num
    mov edi, offset buffer

N19H EQU 00DE0B6B3H
N19L EQU 0A7640000H
N20H EQU 08AC72304H
N20L EQU 089E80000H

Q2Ammx proc uses edi lpBuffer, lpNumber
    mov ecx,lpNumber
    mov eax,[ecx]
    mov edx,[ecx+4]
    mov edi,lpBuffer
    sub esp,12
    xor ecx,ecx
    cmp edx,N19H
    jb     @@nm18
    jne   @@cp20
    cmp eax,N19L
    jb     @@nm18
@@cp20:
    cmp edx,N20H
    jb     @@do19
    jne   @@do20
    cmp eax,N20L
    jb     @@do19
@@do20:
    mov cl,10h-1
    sub  eax,N20L
    sbb edx,N20H
@@lp19:
    inc ecx
@@do19:
    sub eax,N19L
    sbb edx,N19H
    jae  @@lp19
    add eax,N19L
    adc edx,N19H
@@nm18:
    push edx
    push eax
    fild qword ptr [esp]
    fbstp [esp]
    mov [esp+9],ecx

    mov edx,2
    cmp dword ptr [esp+8],0
    jne @@dg16
    cmp dword ptr [esp+4],1
    sbb edx,1
@@dg16:
    bsr ecx,[esp+edx*4]
    je @@zero
    shr ecx,3
    lea eax,[ecx+4*edx]
    lea edx,[eax+eax]
    cmp byte ptr [esp][eax],10h
    sbb edx,-1

    movq mm(7),mmxb0F
    movq mm(6),mmxb30
    movq mm(0),[esp]
    movq mm(4),[esp+8]
    movq mm(1),mm(0)
    psrlq mm(1),4
    pand mm(0),mm(7)
    por    mm(0),mm(6)
    pand mm(1),mm(7)
    por mm(1),mm(6)
    movq mm(2),mm(0)
    punpcklbw mm(0),mm(1)
    movq [esp],mm(0)
    punpckhbw mm(2),mm(1)
    movq [esp+8],mm(2)
    movq mm(5),mm(4)
    psrlq mm(5),4
    pand mm(4),mm(7)
    por mm(4),mm(6)
    pand mm(5),mm(7)
    por mm(5),mm(6)
    punpcklbw mm(4),mm(5)
    movd [esp+16],mm(4)
    emms
@@lpS:
    mov eax,[esp+edx-3]
    bswap eax
    mov [edi],eax
    add edi,4
    sub edx,4
    jns  @@lpS
    mov byte ptr [edi][edx][1],0
    add esp,20
    ret
@@zero:
    mov dword ptr [edi],'0'
    add esp,20
    ret
Q2Ammx endp

    end
64 bit to dec ASCIIZ - 2 with MMX (http://www.madwizard.org/programming/snippets?id=26)
Code: [Select]
.586
.MMX
.model flat,stdcall
option casemap:none

.data
ALIGN 8
mmxb0F dq 0F0F0F0F0F0F0F0Fh
mmxb30 dq 3030303030303030h

.code
Q2Ammxf proc uses edi lpBuffer,lpNumber
    mov ecx,lpNumber
    mov edi,lpBuffer
    mov eax,[ecx]
    mov edx,[ecx+4]

D05H    equ 045639182h
D05L    equ 044F40000h
D04H    equ 03782DACEh
D04L    equ 09D900000h
D01H    equ 00DE0B6B3h
D01L    equ 0A7640000h
QtoA:
    sub esp,12
    xor ecx,ecx
    sub eax,D01L
    sbb edx,D01H
    jb    @@a01f
    sub eax,D04L
    sbb edx,D04H
    jb    @@a05
    mov cl,05h
    sub eax,D05L
    sbb edx,D05H
    jb    @@a05
    mov cl,15h
    sub eax,D05L
    sbb edx,D05H
    jae  @@l01
    mov cl,10h
@@a05:
    add eax,D05L
    adc edx,D05H
@@l01:  inc ecx
    sub eax,D01L
    sbb edx,D01H
    jae  @@l01
    dec ecx
@@a01f:
    add eax,D01L
    adc edx,D01H

    push edx
    push eax
    fild qword ptr [esp]
    fbstp [esp]
    mov [esp+9],ecx
    mov edx,2
    cmp dword ptr [esp+8],0
    jne @@dg16
    cmp dword ptr [esp+4],1
    sbb edx,1
@@dg16:
    bsr ecx,[esp+edx*4]
    je @@zero
    shr ecx,3
    lea eax,[ecx+4*edx]
    lea edx,[eax+eax]
    cmp byte ptr [esp][eax],10h
    sbb edx,-1

    movq mm(7),mmxb0F
    movq mm(6),mmxb30
    movq mm(0),[esp]
    movq mm(4),[esp+8]
    movq mm(1),mm(0)
    psrlq mm(1),4
    pand mm(0),mm(7)
    por    mm(0),mm(6)
    pand mm(1),mm(7)
    por mm(1),mm(6)
    movq mm(2),mm(0)
    punpcklbw mm(0),mm(1)
    movq [esp],mm(0)
    punpckhbw mm(2),mm(1)
    movq [esp+8],mm(2)
    movq mm(5),mm(4)
    psrlq mm(5),4
    pand mm(4),mm(7)
    por mm(4),mm(6)
    pand mm(5),mm(7)
    por mm(5),mm(6)
    punpcklbw mm(4),mm(5)
    movd [esp+16],mm(4)
    emms
@@lpS:
    mov eax,[esp+edx-3]
    bswap eax
    mov [edi],eax
    add edi,4
    sub edx,4
    jns  @@lpS
    mov byte ptr [edi][edx][1],0
    add esp,20
    ret
@@zero:
    mov dword ptr [edi],'0'
    add esp,20
    ret
Q2Ammxf endp
    end
Title: Re: Integer to ascii algorithm.
Post by: fearless on April 12, 2016, 08:29:49 AM
Does anyone have a function similar to these that converts a qword 64bit value to ascii hex? ive searched the forums here and other places but cant seem to find anything, so just wondering if someone has one that they could share. Cheers.
Title: Re: Integer to ascii algorithm.
Post by: jj2007 on April 12, 2016, 10:10:51 AM
Does anyone have a function similar to these that converts a qword 64bit value to ascii hex?

include \masm32\MasmBasic\MasmBasic.inc      ; download (http://masm32.com/board/index.php?topic=94.0)
  SetGlobals MyQ:QWORD=1234567812345678h
  Init
  Inkey "MyQ=", Hex$(MyQ)
EndOfCode