Hi,
On my currency if you want to write 1 million is 1.000.000 but on English it was 1,000,000 is there any function to format the string to my currency format?
http://msdn.microsoft.com/en-us/library/dd318083.aspx
We worked for a while on this matter on this thread:
http://www.masm32.com/board/index.php?topic=13939.0
Ciao
And if you speak C, this is the C version that does the same for
integer number only:
//-----------------------------------------------------------------------------------------------
// Function prototype for itoas(). Takes in the number to
// format, the pointer of the buffer to fill, and the thousand separator
// to use. Returns the lenght of the generated string.
//-----------------------------------------------------------------------------------------------
int itoas(long num, char [], char sep);
and:
//---------------------------------------------------------------
// itoas() - iMalc version updated ver. 0.8
//---------------------------------------------------------------
int itoas(long snum, char s[], char sep)
{
char *ps = s;
unsigned long num1 = snum, num2, num3, div;
if (snum < 0) {
*ps++ = '-';
num1 = -snum;
}
if (num1 < 10000) {
if (num1 < 10) goto L1;
if (num1 < 100) goto L2;
if (num1 < 1000) goto L3;
} else {
num2 = num1 / 10000;
num1 -= num2 * 10000;
if (num2 < 10000) {
if (num2 < 10) goto L5;
if (num2 < 100) goto L6;
if (num2 < 1000) goto L7;
} else {
num3 = num2 / 10000;
num2 -= num3 * 10000;
if (num3 >= 10) {
*ps++ = '0' + (char)(div = (num3*6554UL)>>16); num3 -= div*10;
*ps++ = sep;
}
*ps++ = '0' + (char)(num3);
}
*ps++ = '0' + (char)(div = (num2*8389UL)>>23); num2 -= div*1000;
L7:
*ps++ = '0' + (char)(div = (num2*5243UL)>>19); num2 -= div*100;
*ps++ = sep;
L6:
*ps++ = '0' + (char)(div = (num2*6554UL)>>16); num2 -= div*10;
L5:
*ps++ = '0' + (char)(num2);
}
*ps++ = '0' + (char)(div = (num1*8389UL)>>23); num1 -= div*1000;
*ps++ = sep;
L3:
*ps++ = '0' + (char)(div = (num1*5243UL)>>19); num1 -= div*100;
L2:
*ps++ = '0' + (char)(div = (num1*6554UL)>>16); num1 -= div*10;
L1:
*ps++ = '0' + (char)(num1);
*ps = '\0';
return ps - s;
}
to adapt it to your needs, or to translate it into ASM is up to you.
I didn't translate it yet.
Enjoy
Quote from: frktons on July 27, 2010, 12:48:54 PM
And if you speak C, this is the C version that does the same for
integer number only:
//-----------------------------------------------------------------------------------------------
// Function prototype for itoas(). Takes in the number to
// format, the pointer of the buffer to fill, and the thousand separator
// to use. Returns the lenght of the generated string.
//-----------------------------------------------------------------------------------------------
int itoas(long num, char [], char sep);
and:
//---------------------------------------------------------------
// itoas() - iMalc version updated ver. 0.8
//---------------------------------------------------------------
int itoas(long snum, char s[], char sep)
{
char *ps = s;
unsigned long num1 = snum, num2, num3, div;
if (snum < 0) {
*ps++ = '-';
num1 = -snum;
}
if (num1 < 10000) {
if (num1 < 10) goto L1;
if (num1 < 100) goto L2;
if (num1 < 1000) goto L3;
} else {
num2 = num1 / 10000;
num1 -= num2 * 10000;
if (num2 < 10000) {
if (num2 < 10) goto L5;
if (num2 < 100) goto L6;
if (num2 < 1000) goto L7;
} else {
num3 = num2 / 10000;
num2 -= num3 * 10000;
if (num3 >= 10) {
*ps++ = '0' + (char)(div = (num3*6554UL)>>16); num3 -= div*10;
*ps++ = sep;
}
*ps++ = '0' + (char)(num3);
}
*ps++ = '0' + (char)(div = (num2*8389UL)>>23); num2 -= div*1000;
L7:
*ps++ = '0' + (char)(div = (num2*5243UL)>>19); num2 -= div*100;
*ps++ = sep;
L6:
*ps++ = '0' + (char)(div = (num2*6554UL)>>16); num2 -= div*10;
L5:
*ps++ = '0' + (char)(num2);
}
*ps++ = '0' + (char)(div = (num1*8389UL)>>23); num1 -= div*1000;
*ps++ = sep;
L3:
*ps++ = '0' + (char)(div = (num1*5243UL)>>19); num1 -= div*100;
L2:
*ps++ = '0' + (char)(div = (num1*6554UL)>>16); num1 -= div*10;
L1:
*ps++ = '0' + (char)(num1);
*ps = '\0';
return ps - s;
}
to adapt it to your needs, or to translate it into ASM is up to you.
I didn't translate it yet.
Enjoy
I prefer Ghandi method. Easier and simpler.
Quote from: Farabi on July 27, 2010, 01:47:33 PM
I prefer Ghandi method. Easier and simpler.
The bazaar is open till midnight, choose whatever you like :P
By the way, the C version is the fastest, in case you need this info. :U
But I want to know which one is the float point of your currency? :eek
Onan,
Just get the length of the currency string, see how many 3 character blocks are in it them write the string to another larger buffer with the added characters for your currency format.
here is another way to do it as long as there is no fraction and decimal point. IE "1234567890" but not "1234567890.00".
IF 0 ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Build this template with "CONSOLE ASSEMBLE AND LINK"
ENDIF ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm32\include\masm32rt.inc
fmtstr PROTO :DWORD,:DWORD
.data
curr db "1234567890",0
buff db " ",0
pcur dd curr
pbuf dd buff
.code
start:
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
call main
inkey
exit
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
main proc
invoke fmtstr,pcur,pbuf
print pbuf,13,10
ret
main endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
fmtstr proc psrc:DWORD,pdst:DWORD
push ebx
invoke szRev,psrc,psrc ; overwrite original
mov ecx, psrc
mov edx, pdst
xor ebx, ebx
lbl0:
mov al, [ecx]
test al, al
jz lbl2
add ecx, 1
cmp ebx, 3
jne lbl1
; ---------------------------------------
; add the seperator every third character
; ---------------------------------------
mov BYTE PTR [edx], "," ; put your preferred character here.
add edx, 1
xor ebx, ebx ; reset counter to zero
; ---------------------------------------
lbl1:
add ebx, 1
mov [edx], al
add edx, 1
jmp lbl0
lbl2:
mov BYTE PTR [edx], 0 ; terminate output buffer
invoke szRev,pdst,pdst ; reverse the output buffer
pop ebx
ret
fmtstr endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end start
Quote from: hutch-- on July 28, 2010, 05:36:38 AM
Onan,
Just get the length of the currency string, see how many 3 character blocks are in it them write the string to another larger buffer with the added characters for your currency format.
Yeah I did. Thanks anyway.
Quote from: hoverlees on July 28, 2010, 05:31:19 AM
But I want to know which one is the float point of your currency? :eek
Many countries use the coma as the decimal delimiter (instead of the "decimal point").
Quote from: raymond on July 29, 2010, 01:24:50 AM
Quote from: hoverlees on July 28, 2010, 05:31:19 AM
But I want to know which one is the float point of your currency? :eek
Many countries use the coma as the decimal delimiter (instead of the "decimal point").
thanks :bdg
As you notice my english is not so good
No offence, but Isn't it that you are trying to achieve ?
Quote from: Geryon on July 29, 2010, 09:22:43 AM
As you notice my english is not so good
No offence, but Isn't it that you are trying to achieve ?
Yes sir. That's exactly what farabi was looking for. Maybe he doesn't like
calling APIs functions, so he preferred to use an hand-made solution. :P
Any reason you used
test.bat instead of
test.asm?
And a last thing, in order to compile with qeditor and MASM32 package,
I had to modidy some small things:
;---------------------------------------------------------------------------------
; Format currency values with thousand separator, decimal point and
; currency symbol based on LOCALE_USER_DEFAULT.
;---------------------------------------------------------------------------------
.nolist
include \masm32\include\masm32rt.inc
.686
.data
szMoney DB "12345678910", 0
szTitle DB "Currency Test", 0
.data?
szBuffer DB 255 dup(?)
.code
start:
invoke GetCurrencyFormat, LOCALE_USER_DEFAULT \
, 0 \
, offset szMoney \
, NULL \
, offset szBuffer \
, 255
invoke MessageBox, NULL \
, offset szBuffer \
, offset szTitle \
, MB_OK or MB_SYSTEMMODAL
invoke ExitProcess, -1
end start
It was ready for
EasyCode, probably not for
qeditor ::)
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
Quote from: frktons on July 29, 2010, 11:57:27 AM
Any reason you used test.bat instead of test.asm?
Yes, when I double click that .bat file, it will begin to execute masm compile commands.
So, without a IDE, it provide easy build.
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
Thank you, I will do that.
Acutally, I've been always confused about that.
Let's take realy long line
invoke Function, arg1, arg2, arg3, arrrrrrrrrg4, arg5, ..... etc
How should I divide it ?
like that (C style)
invoke function arg1,
arg2,
arg3,
etc...
or like this
invoke function arg1, arg2, arg3
arg4, arg5, arg6
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.
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. " )
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.
:bg
Looking at the code posted to do the job, now you know why I roll my own. :P
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
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?
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.
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.