Author Topic: Integer to ascii algorithm.  (Read 4533 times)

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 4814
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Integer to ascii algorithm.
« 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
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :biggrin:

dedndave

  • Member
  • *****
  • Posts: 8734
  • Still using Abacus 2.0
    • DednDave
Re: Integer to ascii algorithm.
« Reply #1 on: March 15, 2015, 02:10:15 AM »
signed or unsigned ?   :redface:

Vortex

  • Member
  • *****
  • Posts: 1704
Re: Integer to ascii algorithm.
« Reply #2 on: March 15, 2015, 05:45:38 AM »
Hi Dave,

It will handle both signed and unsigned 32 bit integers

dedndave

  • Member
  • *****
  • Posts: 8734
  • Still using Abacus 2.0
    • DednDave
Re: Integer to ascii algorithm.
« Reply #3 on: March 15, 2015, 06:13:00 AM »
lol
well, yah - unsigned if below 2147483648
that is what we call "signed integers"

Antariy

  • Member
  • ****
  • Posts: 541
Re: Integer to ascii algorithm.
« Reply #4 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.

dedndave

  • Member
  • *****
  • Posts: 8734
  • Still using Abacus 2.0
    • DednDave
Re: Integer to ascii algorithm.
« Reply #5 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"

Antariy

  • Member
  • ****
  • Posts: 541
Re: Integer to ascii algorithm.
« Reply #6 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

dedndave

  • Member
  • *****
  • Posts: 8734
  • Still using Abacus 2.0
    • DednDave
Re: Integer to ascii algorithm.
« Reply #7 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

Antariy

  • Member
  • ****
  • Posts: 541
Re: Integer to ascii algorithm.
« Reply #8 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.

dedndave

  • Member
  • *****
  • Posts: 8734
  • Still using Abacus 2.0
    • DednDave
Re: Integer to ascii algorithm.
« Reply #9 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

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 4814
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Integer to ascii algorithm.
« Reply #10 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
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :biggrin:

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 4814
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Integer to ascii algorithm.
« Reply #11 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
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :biggrin:

dedndave

  • Member
  • *****
  • Posts: 8734
  • Still using Abacus 2.0
    • DednDave
Re: Integer to ascii algorithm.
« Reply #12 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

nidud

  • Member
  • *****
  • Posts: 1371
    • https://github.com/nidud/asmc
Re: Integer to ascii algorithm.
« Reply #13 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

LarryC

  • Regular Member
  • *
  • Posts: 9
Re: Integer to ascii algorithm.
« Reply #14 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.