News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

The Funny stack

Started by frktons, September 20, 2010, 10:22:10 PM

Previous topic - Next topic

frktons

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
Mind is like a parachute. You know what to do in order to use it :-)

KeepingRealBusy

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.

frktons

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
Mind is like a parachute. You know what to do in order to use it :-)

KeepingRealBusy

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

frktons

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
Mind is like a parachute. You know what to do in order to use it :-)

frktons

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
Mind is like a parachute. You know what to do in order to use it :-)

Antariy

Frank, when you would implement your test-bed, then include the code for thousand separators also. This makes timings more readable.  :U



Alex

frktons

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
Mind is like a parachute. You know what to do in order to use it :-)

Antariy

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

jj2007

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.

FORTRANS

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.

frktons

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
Mind is like a parachute. You know what to do in order to use it :-)

frktons

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
Mind is like a parachute. You know what to do in order to use it :-)