The MASM Forum Archive 2004 to 2012

General Forums => The Campus => Topic started by: frktons on September 20, 2010, 10:22:10 PM

Title: The Funny stack
Post by: frktons on September 20, 2010, 10:22:10 PM
Trying to use the stack in order to insert thousand separator in a numeric string
I got this:

Quote
The original string: 3365417345

The reversed/formatted string: 543.714.563.3

The rereversed/formatted string: 3.365.417.345

Press any key to close...

It's funny and resource consuming.
So I'm going to experiment many possible alternative ways to do
the task: inserting a thousand separator in a unsigned DWORD.

I think some dozen ways are possible.

Fell free to post any funny/alternative/classic way to get the job done.

Attached the funny version

Frank
Title: Re: The Funny stack
Post by: KeepingRealBusy on September 20, 2010, 10:38:46 PM
Frank,

What I did was broke the number into pieces of < 1000 (magic number multiply for divide by 1000), then converted each digit of the 999 by magic number multiply for divide by 10, then inserted the separator (if more preceeded but not yet converted), then divided by 1000 again...   Stop when the dividend is 0. This gives you up to a 14 digit number (x,xxx,xxx,xxxNULL) which is Right justified in a 14 BYTE area. Left justify this by SSE unaligned load from the MSD (insure that the 14 BYTE work area is followed by 16 0's), then aligned/unaligned store depending on the alignment of the destination (only if necessary - you may just want to return the offset of the msd without moving).

Dave.
Title: Re: The Funny stack
Post by: frktons on September 20, 2010, 11:12:23 PM
Quote from: KeepingRealBusy on September 20, 2010, 10:38:46 PM
Frank,

What I did was broke the number into pieces of < 1000 (magic number multiply for divide by 1000), then converted each digit of the 999 by magic number multiply for divide by 10, then inserted the separator (if more preceeded but not yet converted), then divided by 1000 again...   Stop when the dividend is 0. This gives you up to a 14 digit number (x,xxx,xxx,xxxNULL) which is Right justified in a 14 BYTE area. Left justify this by SSE unaligned load from the MSD (insure that the 14 BYTE work area is followed by 16 0's), then aligned/unaligned store depending on the alignment of the destination (only if necessary - you may just want to return the offset of the msd without moving).

Dave.

Hi Dave,
Thanks for your explanations.
I posted something similar that is done in C and that doesn't need the last part
of realigning on the left the formatted string, maybe I'll slowly try to translate
it into masm. The only problem is that I have limited free time and Assembly knowledge.

By the way, if you don't mind, post the code as well, with a little example like
mine, to show how it works.

Or just add it to the code I posted.

In a few weeks a new testbed will come up with colors, boxes, grids, and a better
looking display for the results.

I'll use the routines that will be posted here to test the new testbed and to prepare it
for the future use on the forum, according to the comments and advices I get.

Frank
Title: Re: The Funny stack
Post by: KeepingRealBusy on September 20, 2010, 11:30:20 PM
Frank,

Here is the function less the data definitions.

Dave.


;-------------------------------------------------------------------------------
;
;   Convert a binary number to 14 decimal digits with separators.
;   Enter with number pointer in edx (Lo/Hi), buffer pointer (16 digit) in edi.
;   Return with number converted and edi pointing to MSD that is non-zero. The
;   leading characters (in front of edi) are blanked, so you can write the 16
;   digit, right justified, number at the original edi OFFSET, or only the
;   significant digits using the returned edi OFFSET.
;
;-------------------------------------------------------------------------------

BTD PROC USES eax ebx ecx edx esi

    mov    eax,[edx]                        ;   Get Low.
    mov    edx,[edx+4]                      ;   Get High.
    mov    ebx,dBillion                     ;   Get billion.
    div    ebx                              ;   Convert to billions (eax) and fraction (edx).
    mov    dBillions,eax                    ;   Save the billions.
    mov    eax,edx                          ;   Convert the Low value.
;
;Get big memory block,          size = 999,000,000,000 BYTES, the elapsed time is:        0.42
;
;   Convert fraction then billions.
;
    mov    esi,eax                          ;   Convert the Low value.
    mov    ecx,14                           ;   Set the character position for low 9 digits.
    mov    ebx,2
;
;   Convert by multiplying.
;
Cvt:
    mov    edx,3435973837                   ;   Get magic number for divisor of 10.
    mul    edx                              ;   edx = (quotient * 8) + garbage.
    shr    edx,3                            ;   edx = quotient
    lea    eax,[edx+edx*4]                  ;   eax = quotient * 5.
    shl    eax,1                            ;   eax = quotient * 10.
    neg    eax                              ;   eax = - quotient * 10.
    lea    eax,[esi+eax+'0']                ;   eax = LSD
    mov    [edi+ecx],al                     ;   Save digit.
    mov    eax,edx                          ;   eax = quotient.
    mov    esi,eax                          ;   Save quotient.
    dec    ecx                              ;   Point to prior digit space.
    dec    ebx                              ;   Decrement 1000's
    jns    Cnt                              ;   Not there.
    dec    ecx                              ;   Skip comma.
    lea    ebx,[ebx+3]                      ;   Set for next 1000.
Cnt:
    cmp    ecx,2                            ;   Offset for the billions?
    jg     Cvt                              ;   No, not done with Low conversion.
    je     GetHigh                          ;   Yes, get the high value.
    or     ecx,ecx                          ;   Total conversion complete?
    jns    Cvt                              ;   No, keep converting High.
    jmp    Separate                         ;   Yes.
GetHigh:
    mov    eax,dBillions                    ;   Get billions value.
    mov    esi,eax                          ;   Convert the High value 3 digits.
    jmp    Cvt
;
;   Separate with commas.
;
Separate:
    mov    BYTE PTR [edi+3],','
    mov    BYTE PTR [edi+7],','
    mov    BYTE PTR [edi+11],','
    jmp    ScanNonZero                      ;   Scan for decimal digit > 0
;
;   Blank leading commas and leading zeros, not last zero.
;
BlankFill:
    mov    BYTE PTR [edi],' '               ;   Blank the character.
    inc    edi                              ;   Point to the next character.
ScanNonZero:
    mov    al,[edi]                         ;   Get the character.
    cmp    al,','                           ;   Is it a leading comma?
    jz     BlankFill                        ;   Yes, blank it.
    cmp    al,'0'                           ;   Is it a leading '0'?
    ja     Exit                             ;   No, a digit > '0'. done with blanking.
    or     al,al                            ;   Is it a trailing null?
    jnz    BlankFill                        ;   No, blank it.
    dec    edi                              ;   Point to the last character.
    mov    BYTE PTR [edi],'0'               ;   Force the '0' back.
;
;   Exit BTD.
;
Exit:
    ret
BTD ENDP
Title: Re: The Funny stack
Post by: frktons on September 20, 2010, 11:40:43 PM
Quote from: KeepingRealBusy on September 20, 2010, 11:30:20 PM
Frank,

Here is the function less the data definitions.

Dave.


;-------------------------------------------------------------------------------
;
;   Convert a binary number to 14 decimal digits with separators.
;   Enter with number pointer in edx (Lo/Hi), buffer pointer (16 digit) in edi.
;   Return with number converted and edi pointing to MSD that is non-zero. The
;   leading characters (in front of edi) are blanked, so you can write the 16
;   digit, right justified, number at the original edi OFFSET, or only the
;   significant digits using the returned edi OFFSET.
;
;-------------------------------------------------------------------------------

BTD PROC USES eax ebx ecx edx esi

    mov    eax,[edx]                        ;   Get Low.
    mov    edx,[edx+4]                      ;   Get High.
    mov    ebx,dBillion                     ;   Get billion.
    div    ebx                              ;   Convert to billions (eax) and fraction (edx).
    mov    dBillions,eax                    ;   Save the billions.
    mov    eax,edx                          ;   Convert the Low value.
;
;Get big memory block,          size = 999,000,000,000 BYTES, the elapsed time is:        0.42
;
;   Convert fraction then billions.
;
    mov    esi,eax                          ;   Convert the Low value.
    mov    ecx,14                           ;   Set the character position for low 9 digits.
    mov    ebx,2
;
;   Convert by multiplying.
;
Cvt:
    mov    edx,3435973837                   ;   Get magic number for divisor of 10.
    mul    edx                              ;   edx = (quotient * 8) + garbage.
    shr    edx,3                            ;   edx = quotient
    lea    eax,[edx+edx*4]                  ;   eax = quotient * 5.
    shl    eax,1                            ;   eax = quotient * 10.
    neg    eax                              ;   eax = - quotient * 10.
    lea    eax,[esi+eax+'0']                ;   eax = LSD
    mov    [edi+ecx],al                     ;   Save digit.
    mov    eax,edx                          ;   eax = quotient.
    mov    esi,eax                          ;   Save quotient.
    dec    ecx                              ;   Point to prior digit space.
    dec    ebx                              ;   Decrement 1000's
    jns    Cnt                              ;   Not there.
    dec    ecx                              ;   Skip comma.
    lea    ebx,[ebx+3]                      ;   Set for next 1000.
Cnt:
    cmp    ecx,2                            ;   Offset for the billions?
    jg     Cvt                              ;   No, not done with Low conversion.
    je     GetHigh                          ;   Yes, get the high value.
    or     ecx,ecx                          ;   Total conversion complete?
    jns    Cvt                              ;   No, keep converting High.
    jmp    Separate                         ;   Yes.
GetHigh:
    mov    eax,dBillions                    ;   Get billions value.
    mov    esi,eax                          ;   Convert the High value 3 digits.
    jmp    Cvt
;
;   Separate with commas.
;
Separate:
    mov    BYTE PTR [edi+3],','
    mov    BYTE PTR [edi+7],','
    mov    BYTE PTR [edi+11],','
    jmp    ScanNonZero                      ;   Scan for decimal digit > 0
;
;   Blank leading commas and leading zeros, not last zero.
;
BlankFill:
    mov    BYTE PTR [edi],' '               ;   Blank the character.
    inc    edi                              ;   Point to the next character.
ScanNonZero:
    mov    al,[edi]                         ;   Get the character.
    cmp    al,','                           ;   Is it a leading comma?
    jz     BlankFill                        ;   Yes, blank it.
    cmp    al,'0'                           ;   Is it a leading '0'?
    ja     Exit                             ;   No, a digit > '0'. done with blanking.
    or     al,al                            ;   Is it a trailing null?
    jnz    BlankFill                        ;   No, blank it.
    dec    edi                              ;   Point to the last character.
    mov    BYTE PTR [edi],'0'               ;   Force the '0' back.
;
;   Exit BTD.
;
Exit:
    ret
BTD ENDP


Thanks Dave,

when the new testbed will be ready, if I need any explanation, I'll tell you.

And attached a second version using the API GetNumberFormat

Frank
Title: Re: The Funny stack
Post by: frktons on September 21, 2010, 11:23:44 PM
And here we have a version Hutch posted some weeks ago.
The collection is quite big, stay tuned, I'll have to put them
all together and test them with the new testbed that is in the
design stage:
http://www.masm32.com/board/index.php?topic=14871.0

Frank
Title: Re: The Funny stack
Post by: Antariy on September 21, 2010, 11:47:26 PM
Frank, when you would implement your test-bed, then include the code for thousand separators also. This makes timings more readable.  :U



Alex
Title: Re: The Funny stack
Post by: frktons on September 21, 2010, 11:59:25 PM
Quote from: Antariy on September 21, 2010, 11:47:26 PM
Frank, when you would implement your test-bed, then include the code for thousand separators also. This makes timings more readable.  :U
Alex


Of course, I need a good new CPUID code, the macro for timings, the thousand
separator routine, and so on.

Working on it,....slowly  :lol

Frank
Title: Re: The Funny stack
Post by: Antariy on September 22, 2010, 12:09:08 AM
Quote from: frktons on September 21, 2010, 11:59:25 PM

Of course, I need a good new CPUID code, the macro for timings, the thousand
separator routine, and so on.

Working on it,....slowly  :lol

Frank

I know too little peoples in forum, but I think, what Jochen can make very good macro for you, or explain how make it.



Alex
Title: Re: The Funny stack
Post by: jj2007 on September 22, 2010, 06:52:14 AM
Quote from: Antariy on September 22, 2010, 12:09:08 AMJochen can make very good macro for you, or explain how make it.

My macros are for public use, no problem. Search the forum for CodeSize. However, I won't have the time to develop a thousand separators macro, that is pretty challenging - maybe Frank can volunteer? Besides, as for myself I prefer to use a shl eax, 8 and call them kCycles.
Title: Re: The Funny stack
Post by: FORTRANS on September 22, 2010, 12:07:59 PM
Hi,

QuoteBesides, as for myself I prefer to use a shl eax, 8 and call them kCycles.

   Well, perhaps formatting something like in the thread
"Re: decimal as KiB,Mib,GiB etc." in The Laboratry could be
used?  See your reply #25 or mine #27 for example.

Regards,

Steve N.
Title: Re: The Funny stack
Post by: frktons on September 22, 2010, 03:23:32 PM
Well, I'll have a look at the thread:
http://www.masm32.com/board/index.php?topic=9585.0
and see if there is something suitable for the actual task.

Frank
Title: Re: The Funny stack
Post by: frktons on September 23, 2010, 12:00:10 AM
A version Alex sent to me:

;---------------------------------------------------------------------
; Thousand separator by Alex
;---------------------------------------------------------------------


.nolist
include \masm32\include\masm32rt.inc

.code

OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE
align 16
Separate1000ds proc STDCALL dwNum:DWORD, lpszOutStr:PTR DWORD
;int 3
pop edx ; ret
pop eax ; num
pop ecx ; str

push edx
push ebx
push esi

add esp,-(36)

mov esi,esp

push "ul0%"

push 1000T

mov ebx,esp

jmp @l1

@@:
div dword ptr [ebx]
push edx
mov word ptr [esi],"%." ; format and separator
mov dword ptr [esi+2],"ul30" ; format
add esi,6
@l1:
xor edx,edx
cmp eax,edx
jnz @B

add ebx,4
mov dword ptr [esi-6],edx

push ebx
push ecx

call wsprintf

lea esp,[ebx+40]

pop esi
pop ebx
retn

Separate1000ds endp
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef

align 16
start:

add esp,-1032

invoke crt_printf,repargof("Enter a value please: ")
invoke crt_scanf,repargof("%lu"),esp
test eax,eax
pop ecx
jnz @F
invoke crt_printf,repargof("The input doesn't seem to be a number")
jmp @done
@@:

invoke Separate1000ds,ecx,esp
invoke crt_printf,repargof("Thousand separated number: %s"),esp


@done:
invoke crt__getch
invoke crt_exit

end start


:U