News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Integer to ascii algorithm.

Started by hutch--, March 15, 2015, 12:37:15 AM

Previous topic - Next topic

hutch--

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

dedndave

signed or unsigned ?   :redface:

Vortex


dedndave

lol
well, yah - unsigned if below 2147483648
that is what we call "signed integers"

Antariy

Quote from: dedndave on March 15, 2015, 02:10:15 AM
signed or unsigned ?   :redface:

Dave, do you see it as signed probably?

Erol, this code is not conditional, so, the algo is signed

  ; --------------------------
  ; 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.


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

Quote from: hutch-- on March 15, 2015, 12:37:15 AM
It will handle both signed and unsigned 32 bit integers...

i think it's a "signed routine"

Antariy

Quote from: dedndave on March 15, 2015, 07:01:11 AM
Quote from: hutch-- on March 15, 2015, 12:37:15 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

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

Quote from: 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

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

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--

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--

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

dedndave

Quote from: hutch-- on March 15, 2015, 08:24:16 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

#13
deleted

LarryC

The original signed algorithm was between 20 and 40 cycles for a 1-10 digit conversion including the routine overhead.