News:

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

Formating Signed string

Started by Farabi, July 27, 2010, 11:19:19 AM

Previous topic - Next topic

Farabi

Man I though there is no API for this, anyway I modify Ghandi function to suit my need


FormatString PROC pInput:DWORD,pOutput:DWORD
; Created by Ghandi at MASM Forum
;Get length of string
LOCAL rmnd:dword
LOCAL len:dword
mov edi,pInput
xor eax,eax
or ecx,-1
repne scasb
add ecx,1
not ecx

;Get number of iterations plus remainder
mov eax,ecx
mov ecx,3
xor edx,edx
div ecx
mov rmnd,edx
mov len,eax

;Set source and destination registers, string length and then move across remainder as left most of string
mov esi,pInput
mov edi,pOutput
mov ecx,edx
rep movsb

;Set counter to number of iterations
mov ecx,eax

;Set EDX to AND mask
mov edx,0FFFFFFh

;We'll start each part here with the ',' and then take a full DWORD from EDI as we know there is the NULL byte at the end.
mov ebx,','

@@ThreeCharLoop:
;Save delimiter
.if rmnd!=0
mov byte ptr [edi],bl
add edi,1
.else
.if ecx!=len
mov byte ptr [edi],bl
add edi,1
.endif
.endif
;Load source DWORD
lodsd

;Strip upper byte of EAX with AND mask in EDX
and eax,edx

;Save DWORD to destination as null terminated 3 char string
stosd

;There is a byte overlap from the LODSD/STOSD, decrement source and destination by one byte
sub esi,1
sub edi,1

;Loop like this until ECX == 0
sub ecx,1
jnz @@ThreeCharLoop

;Get length of output string
mov edi,pOutput
xor eax,eax
or ecx,-1
repne scasb
add ecx,1
not ecx

;Set return value
mov eax,ecx
Ret

FormatString EndP


That is your ghandi.
Those who had universe knowledges can control the world by a micro processor.
http://www.wix.com/farabio/firstpage

"Etos siperi elegi"

Farabi

Anyone know how to set the CURRENCYFMT structure?

I tried this but not working

mov cfmt.NumDigits,LOCALE_ICURRDIGITS
mov cfmt.LeadingZero,LOCALE_ILZERO
mov cfmt.Grouping,3
mov cfmt.lpDecimalSep,CTXT(",")
mov cfmt.lpThousandSep,CTXT(".")
mov cfmt.NegativeOrder,LOCALE_INEGCURR
mov cfmt.PositiveOrder,LOCALE_ICURRENCY
mov cfmt.lpCurrencySymbol,CTXT("Rp. " )
Those who had universe knowledges can control the world by a micro processor.
http://www.wix.com/farabio/firstpage

"Etos siperi elegi"

frktons

Quote from: jj2007 on July 29, 2010, 12:41:03 PM
Just a small hint: With invoke, you don't need backslashes, a comma is enough

    invoke GetCurrencyFormat, LOCALE_USER_DEFAULT,
    0,
    offset szMoney,
    NULL,
    offset szBuffer,
    SIZEOF szBuffer


Or, on one line: invoke GetCurrencyFormat, LOCALE_USER_DEFAULT, 0, offset szMoney, NULL, offset szBuffer, SIZEOF szBuffer


Thanks Jochen  :U

Quote from: Farabi on July 30, 2010, 01:55:40 AM
Anyone know how to set the CURRENCYFMT structure?

I tried this but not working

mov cfmt.NumDigits,LOCALE_ICURRDIGITS
mov cfmt.LeadingZero,LOCALE_ILZERO
mov cfmt.Grouping,3
mov cfmt.lpDecimalSep,CTXT(",")
mov cfmt.lpThousandSep,CTXT(".")
mov cfmt.NegativeOrder,LOCALE_INEGCURR
mov cfmt.PositiveOrder,LOCALE_ICURRENCY
mov cfmt.lpCurrencySymbol,CTXT("Rp. " )


Did you include the necessary headers?
Post the entire pgm, it'll be easier to see is something is missing.
Mind is like a parachute. You know what to do in order to use it :-)

hutch--

 :bg

Looking at the code posted to do the job, now you know why I roll my own.  :P
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

frktons

The long searched API to format integer numbers with comma/point separator is:

The GetNumberFormat function formats a number string as a number string customized for a specified locale.

int GetNumberFormat(

    LCID Locale, // locale for which number string is to be formatted
    DWORD dwFlags, // bit flag that controls the function's operation
    LPCTSTR lpValue, // pointer to input number string
    CONST NUMBERFMT *lpFormat, // pointer to a formatting information structure
    LPTSTR lpNumberStr, // pointer to output buffer
    int cchNumber // size of output buffer
   );


Parameters

Locale

Specifies the locale for which the number string is to be formatted. If lpFormat is NULL, the function formats the string according to the number format for this locale. If lpFormat is not NULL, the function uses the locale only for formatting information not specified in the NUMBERFMT structure (for example, the locale's string value for the negative sign).
This parameter can be a locale identifier created by the MAKELCID macro, or one of the following predefined values:

LOCALE_SYSTEM_DEFAULT Default system locale.
LOCALE_USER_DEFAULT Default user locale.


dwFlags

Contains a bit flag that controls the operation of the function. If lpFormat is non-NULL, this parameter must be zero.
If lpFormat is NULL, you can specify the LOCALE_NOUSEROVERRIDE flag to format the string using the system default number format for the specified locale; or you can specify zero to format the string using any user overrides to the locale's default number format

lpValue

Points to a null-terminated string containing the number string to format.
This string can only contain the following characters:

· Characters '0' through '9'
· One decimal point (dot) if the number is a floating-point value
· A minus sign in the first character position if the number is a negative value

All other characters are invalid. The function returns an error if the string pointed to by lpValue deviates from these rules.

lpFormat

Pointer to a NUMBERFMT structure that contains number formatting information. All members in the structure pointed to by lpFormat must contain appropriate values.
If lpFormat is NULL, the function uses the number format of the specified locale.



lpNumberStr

Points to a buffer to receive the formatted number string.

cchNumber

Specifies the size, in bytes (ANSI version) or characters (Unicode version), of the lpNumberStr buffer. If cchNumber is zero, the function returns the number of bytes or characters required to hold the formatted number string, and the buffer pointed to by lpNumberStr is not used.



Return Values

If the function succeeds, the return value is the number of bytes (ANSI version) or characters (Unicode version) written to the buffer pointed to by lpNumberStr, or if the cchNumber parameter is zero, the number of bytes or characters required to hold the formatted number string.
If the function fails, the return value is zero. To get extended error information, call GetLastError. GetLastError may return one of the following error codes:

ERROR_INSUFFICIENT_BUFFER
ERROR_INVALID FLAGS
ERROR_INVALID_PARAMETER



If you don't need the currency sign, or the fractional part used for cents
in currency format, the API of choice should be GetNumberFormat
:U
Mind is like a parachute. You know what to do in order to use it :-)

frktons

And the prog to use GetNumberFormat() API:

;---------------------------------------------------------------------------------
; Format integer values with thousand separator.
; Separator symbol based on LOCALE_USER_DEFAULT.
;---------------------------------------------------------------------------------

.nolist
include \masm32\include\masm32rt.inc

.686

.data
    Number      DB  "12345678910", 0
    BoxTitle      DB  "Num format Test", 0
   
.data?
    Buffer      DB  255 dup(?)

.code
start:
    invoke GetNumberFormat, LOCALE_USER_DEFAULT,
                            0,
                            offset Number,
                            NULL,
                            offset Buffer,
                            255
                           
    invoke MessageBox, NULL,
                       offset Buffer,
                       offset BoxTitle,
                       MB_OK ;  or MB_SYSTEMMODAL
                     
    invoke ExitProcess, -1
end start


There is still a little flaw in the prog. It displays the decimal point and
two zeros at the end of the string, and it is wrong. On my pc it should be
a decimal comma, the point is used for thousand separator.

A last thing. If I want to get rid of the decimal part altogether, what have
I to change in the prog?  ::)

Edit: I tried to use LOCALE_SYSTEM_DEFAULT instead of LOCALE_USER_DEFAULT
but it is not defined, I guess in the windows include  ::)

In windows.inc I found:

LOCALE_USER_DEFAULT                  equ 0400h

Could it depends on it the decimal point in my Italian PC?
Mind is like a parachute. You know what to do in order to use it :-)

frktons

Well, probably I have to play a little with these structure fields:

NUMBERFMTA STRUCT
  NumDigits         DWORD      ?
  LeadingZero       DWORD      ?
  Grouping          DWORD      ?
  lpDecimalSep      DWORD      ?
  lpThousandSep     DWORD      ?
  NegativeOrder     DWORD      ?
NUMBERFMTA ENDS

NUMBERFMT  equ  <NUMBERFMTA>

to get the job done.  :P
According to Win API doc:

The NUMBERFMT structure contains information that defines the format of a number string. The GetNumberFormat function uses this information to customize a number string for a specified locale.

typedef struct _numberfmt { 
    UINT      NumDigits;
    UINT      LeadingZero;
    UINT      Grouping;
    LPTSTR    lpDecimalSep;
    LPTSTR    lpThousandSep;
    UINT      NegativeOrder;
} NUMBERFMT;


Members

NumDigits

Specifies the number of fractional digits. This is equivalent to the locale information specified by the LCTYPE constant value LOCALE_IDIGITS.

LeadingZero

Specifies whether to use leading zeroes in decimal fields. This is equivalent to the locale information specified by the LCTYPE constant value LOCALE_ILZERO.

Grouping

Specifies the size of each group of digits to the left of the decimal. Values in the range 0 - 9 are valid.

lpDecimalSep

Points to a null-terminated decimal separator string.

lpThousandSep

Points to a null-terminated thousand separator string.

NegativeOrder

Specifies the negative number mode. This is equivalent to the locale information specified by the LCTYPE constant value LOCALE_INEGNUMBER.

Remarks

For more information about the LCTYPE constants, see LCTYPE Constants.

Mind is like a parachute. You know what to do in order to use it :-)

frktons

After a bit experimentation I came out with this working solution:

;---------------------------------------------------------------------------------
; Format integer values with thousand separator.
; Separator symbol and decimal symbol based on NUMBERFMT structure.
;---------------------------------------------------------------------------------
; format_number2.asm
;---------------------------------------------------------------------------------

.nolist
include \masm32\include\masm32rt.inc

.686

.data
    Number      DB  "12345678910", 0
    BoxTitle    DB  "Num format Test", 0
    Sep         DB  ",",0
    lpSep       DD  Sep
    Tsep        DD  ".",0
    lpTsep      DD  Tsep 
 
   
.data?
    Buffer      DB  255 dup(?)
    NumFmt      NUMBERFMT <>
;---------------------------------------------------------------------------------
; Structure for number formatting
;---------------------------------------------------------------------------------   
; NUMBERFMTA STRUCT
;    NumDigits         DWORD      ?
;    LeadingZero       DWORD      ?
;    Grouping          DWORD      ?
;    lpDecimalSep      DWORD      ?
;    lpThousandSep     DWORD      ?
;    NegativeOrder     DWORD      ?
; NUMBERFMTA ENDS
;
; NUMBERFMT  equ  <NUMBERFMTA>     
;---------------------------------------------------------------------------------
.code
start:

    mov NumFmt.NumDigits, 0               ; No decimal digits
    mov NumFmt.LeadingZero, 0             ; No leading zeroes
    mov NumFmt.Grouping, 3                ; Groups of three numbers
    mov NumFmt.lpDecimalSep,  offset Sep  ; Pointer to NULL terminated string with the decimal separator
    mov NumFmt.lpThousandSep, offset Tsep ; Pointer to NULL terminated string with the thousand separator
    mov NumFmt.NegativeOrder, 0           ; Negative number mode

    invoke GetNumberFormat, NULL,
                            0,
                            offset Number,
                            offset NumFmt,
                            offset Buffer,
                            255
                           
    invoke MessageBox, NULL,
                       offset Buffer,
                       offset BoxTitle,
                       MB_OK ;  or MB_SYSTEMMODAL
                     
    invoke ExitProcess, -1
end start


Now it displays only the integer value, without trailing decimal zeroes, if not needed,
and with the correct decimal comma  separator if a decimal value is passed.
Mind is like a parachute. You know what to do in order to use it :-)