Page 2 of 3

Posted: Tue Jan 16, 2007 9:32 pm
by Flype
hmm, it will not be an easy step... i'll try.

Posted: Tue Jan 16, 2007 10:36 pm
by Flype
I got it :P

I manage to put everything into 1 procedure.

But of course there's 2 procedures. 1 for Quads and 1 for Doubles.

I have to say that it is not a common programming-style.
I use some Reserved arguments to store some variables from one iteration to the next iteration.
It's often used in functional programming languages (but not in Basics).

So this time i think the result is very close to the Win32 one (about functionalities).

And only 12 clean lines per function :)

Code: Select all

; StrNumQ(Number.q [, Grouping.l [, ThousandSep.s]])
; StrNumD(Number.d [, Grouping.l [, ThousandSep.s [, DecimalSep.s [, NumDigits.l]]]])

EnableExplicit

Procedure.s StrNumQ(Number.q, Grouping.l = 3, ThousandSep.s = ",", Reserved1.s = "", Reserved2.s = "", Reserved3.l = 0) 
  If Not Reserved3
    ProcedureReturn StrNumQ(0, Grouping, ThousandSep, StrQ(Number), "", 1)
  EndIf
  If Not Reserved1 Or Reserved1 = "-"
    ProcedureReturn Reserved1 + Reserved2
  EndIf
  If Reserved3 = 2
    Reserved2 = ThousandSep + Reserved2
  EndIf
  ProcedureReturn StrNumQ(0, Grouping, ThousandSep, Left(Reserved1, Len(Reserved1)-Grouping), Right(Reserved1, Grouping) + Reserved2, 2)
EndProcedure

Procedure.s StrNumD(Number.d, Grouping.l = 3, ThousandSep.s = ",", DecimalSep.s = ".", NumDigits.l = 4, Reserved1.s = "", Reserved2.s = "", Reserved3.l = 0) 
  If Not Reserved3
    ProcedureReturn StrNumD(0, Grouping, ThousandSep, "", 0, StringField(StrD(Number), 1, "."), DecimalSep + StringField(StrD(Number, NumDigits), 2, "."), 1)
  EndIf
  If Not Reserved1 Or Reserved1 = "-"
    ProcedureReturn Reserved1 + Reserved2
  EndIf
  If Reserved3 = 2
    Reserved2 = ThousandSep + Reserved2
  EndIf
  ProcedureReturn StrNumD(0, Grouping, ThousandSep, "", 0, Left(Reserved1, Len(Reserved1)-Grouping), Right(Reserved1, Grouping) + Reserved2, 2)
EndProcedure

Debug "StrNumQ()" ;{
Debug StrNumQ(1234567890)
Debug StrNumQ(123456789)
Debug StrNumQ(12345678)
Debug StrNumQ(1234567)
Debug StrNumQ(123456)
Debug StrNumQ(12345)
Debug StrNumQ(1234)
Debug StrNumQ(123)
Debug StrNumQ(12)
Debug StrNumQ(1)
Debug StrNumQ(0)
Debug StrNumQ(-1)
Debug StrNumQ(-12)
Debug StrNumQ(-123)
Debug StrNumQ(-1234)
Debug StrNumQ(-12345)
Debug StrNumQ(-123456)
Debug StrNumQ(-1234567)
Debug StrNumQ(-12345678)
Debug StrNumQ(-123456789)
Debug StrNumQ(-1234567890)
Debug ""
;} 

Debug "StrNumD()" ;{
Debug StrNumD(1234567890.12345)
Debug StrNumD(123456789.12345)
Debug StrNumD(12345678.12345)
Debug StrNumD(1234567.12345)
Debug StrNumD(123456.12345)
Debug StrNumD(12345.12345)
Debug StrNumD(1234.12345)
Debug StrNumD(123.12345)
Debug StrNumD(12.12345)
Debug StrNumD(1.12345)
Debug StrNumD(0.12345)
Debug StrNumD(-1.12345)
Debug StrNumD(-12.12345)
Debug StrNumD(-123.12345)
Debug StrNumD(-1234.12345)
Debug StrNumD(-12345.12345)
Debug StrNumD(-123456.12345)
Debug StrNumD(-1234567.12345)
Debug StrNumD(-12345678.12345)
Debug StrNumD(-123456789.12345)
Debug StrNumD(-1234567890.12345)
Debug ""
;}

End

Posted: Wed Jan 17, 2007 12:19 am
by Paul
Shardik wrote:@Paul:

Thank you for your fine API example. But in the second example there is a small mistake. In order to actually use your defined NUMBERFMT structure you have to provide the pointer to this structure instead of Null:
Thanks for the correction Shardik... cut & paste will get you every time :)

(that's what I get for trying to give 2 examples)

Posted: Thu Jan 18, 2007 8:19 am
by Flype
the fastest StrNumQ() i can do :

Code: Select all

Procedure.s StrNumQ(NumberQ.q, Grouping.l = 3, ThousandSep.s = ",") ; 281
  Protected GroupingNum.l = Pow(10, Grouping), NumberA.q, NumberS.s 
  While NumberQ / GroupingNum 
    NumberA = Abs(NumberQ) 
    NumberS = Str(NumberA % GroupingNum) + ThousandSep + NumberS 
    NumberQ / GroupingNum 
  Wend 
  NumberS = Str(NumberQ) + ThousandSep + NumberS 
  ProcedureReturn Left(NumberS, Len(NumberS) - 1) 
EndProcedure 

Debug "StrNumQ()" ;{
Debug StrNumQ(23487523465274653)
Debug StrNumQ(1234567890)
Debug StrNumQ(123456789)
Debug StrNumQ(12345678)
Debug StrNumQ(1234567)
Debug StrNumQ(123456)
Debug StrNumQ(12345)
Debug StrNumQ(1234)
Debug StrNumQ(123)
Debug StrNumQ(12)
Debug StrNumQ(1)
Debug StrNumQ(0)
Debug StrNumQ(-1)
Debug StrNumQ(-12)
Debug StrNumQ(-123)
Debug StrNumQ(-1234)
Debug StrNumQ(-12345)
Debug StrNumQ(-123456)
Debug StrNumQ(-1234567)
Debug StrNumQ(-12345678)
Debug StrNumQ(-123456789)
Debug StrNumQ(-1234567890)
Debug StrNumQ(-23487523465274653)
;} 
[EDIT] corrected.

Posted: Thu Jan 18, 2007 8:59 am
by AND51
> the fastest
and WORST StrNumD() you can do! :wink:
Check this out:

Code: Select all

Debug StrNumQ_D(23487523465274653)
I've never seen such a strange number... :P

Posted: Thu Jan 18, 2007 9:01 am
by Flype
yes i see... sorry :D

it's because i use Int() instead of IntQ() but well there's a mistake anyway so forget it...

Posted: Thu Jan 18, 2007 9:25 am
by Flype
corrected :wink: (10 lines).

Posted: Thu Jan 18, 2007 9:32 am
by AND51
YEHA!!! FINALLY! Now, I created a procedure with *only* 5 lines of code!!

Code: Select all

Procedure.s groupQuad(number.q, groupLen.l=3, separator.s=",")
	Protected num.s=StrQ(number), currentPackage.s=Right(num, groupLen), currentLen.l=Len(num)-Len(currentPackage), result.s=currentPackage
	If currentLen
		result=groupQuad(ValQ(Left(num, currentLen)), groupLen, separator)+separator+result
	EndIf
	ProcedureReturn ReplaceString(result, "0"+separator, "-")
EndProcedure

For n=1 To 5
Debug groupQuad(1234567890, n) 
Debug groupQuad(123456789, n) 
Debug groupQuad(12345678, n) 
Debug groupQuad(1234567, n) 
Debug groupQuad(123456, n) 
Debug groupQuad(12345, n) 
Debug groupQuad(1234, n) 
Debug groupQuad(123, n) 
Debug groupQuad(12, n) 
Debug groupQuad(1, n) 
Debug groupQuad(0, n) 
Debug groupQuad(-1, n) 
Debug groupQuad(-12, n) 
Debug groupQuad(-123, n) 
Debug groupQuad(-1234, n) 
Debug groupQuad(-12345, n) 
Debug groupQuad(-123456, n) 
Debug groupQuad(-1234567, n) 
Debug groupQuad(-12345678, n) 
Debug groupQuad(-123456789, n) 
Debug groupQuad(-1234567890, n) 
Debug ""
Next

Posted: Thu Jan 18, 2007 9:50 am
by Flype
yeah, nice, and recursive...
a little bit slower than my last one but pretty good.

finally my best :

Code: Select all

Procedure.s StrNumQ(NumberQ.q, Grouping.l = 3, ThousandSep.s = ",") 
  Protected GroupingNum.l = Pow(10, Grouping), NumberS.s 
  While NumberQ / GroupingNum 
    NumberS = Str(IntQ(Abs(NumberQ)) % GroupingNum) + ThousandSep + NumberS 
    NumberQ / GroupingNum 
  Wend 
  NumberS = Str(NumberQ) + ThousandSep + NumberS 
  ProcedureReturn Left(NumberS, Len(NumberS) - 1) 
EndProcedure 

Procedure.s StrNumD(NumberD.d, Grouping.l = 3, ThousandSep.s = ",", DecimalSep.s = ".", NumDigits.l = 4) 
  ProcedureReturn StrNumQ(IntQ(NumberD), Grouping, ThousandSep) + DecimalSep + Left(StringField(StrD(NumberD), 2, "."), NumDigits)
EndProcedure 
fascinating...

Posted: Thu Jan 18, 2007 1:45 pm
by Shardik
Here is shorter version of Paul's second API example:

Code: Select all

Procedure.s FormatNumber(Number.Q)
  Protected Buffer.S = Space(128)
  GetNumberFormat_(#LOCALE_USER_DEFAULT, 0, StrQ(Number), 0, @Buffer, Len(Buffer)) 
  ProcedureReturn Buffer 
EndProcedure

SetLocaleInfo_(#LOCALE_USER_DEFAULT, #LOCALE_IDIGITS, @"0")

Debug FormatNumber(1234567890)
It eliminates the need to define a complete NUMBERFMT structure only to get rid of the trailing two decimal places and the point/comma by using this API call:

Code: Select all

SetLocaleInfo_(#LOCALE_USER_DEFAULT, #LOCALE_IDIGITS, @"0")
And it automatically uses all the local settings defined in the user's windows OS like group separator character etc.

Posted: Wed Jan 24, 2007 6:41 pm
by AND51
Paul wrote:API version that uses system defaults for number formatting...

Code: Select all

Procedure.s FormatNumber(Number.q)
  Buffer.s=Space(255)
  GetNumberFormat_(0,0,StrQ(Number),0,@Buffer,Len(Buffer))
  ProcedureReturn Buffer
EndProcedure

API version if you want to customize the formatting...

Code: Select all

Procedure.s FormatNumber(Number.q)
  Buffer.s=Space(255)
  NF.NUMBERFMT\NumDigits=0   ;number of decimal places to use
  NF\Grouping=3   ;how many numbers before a seperator is used
  NF\lpDecimalSep=@"."   ;decimal seperator character
  NF\lpThousandSep=@","   ;group seperator
  NF\NegativeOrder= 1   ;lookup LOCALE_INEGNUMBER for all options
  GetNumberFormat_(0,0,StrQ(Number),NF,@Buffer,Len(Buffer))
  ProcedureReturn Buffer
EndProcedure
How to convert a integer-number (e. g. 12345) to a grouped integer WITHOUT decimals after the comma?

I always get 12.345,00; but I don't want ,00 ...

Posted: Wed Jan 24, 2007 9:15 pm
by Flype
it works well for me.

Code: Select all

Procedure.s FormatNumber1(Number.q)
  Protected Format.NUMBERFMT, NumberStr.s = Space(255)
  Format\Grouping      = 3
  Format\lpDecimalSep  = @"."
  Format\lpThousandSep = @","
  Format\NegativeOrder = 1
  GetNumberFormat_(#Null, #Null, StrQ(Number), @Format, @NumberStr, Len(NumberStr))
  ProcedureReturn NumberStr
EndProcedure 

Procedure.s FormatNumber2(Number.q)
  Protected NumberStr.s = Space(255)
  GetNumberFormat_(#Null, #Null, StrQ(Number), #Null, @NumberStr, Len(NumberStr))
  ProcedureReturn NumberStr
EndProcedure 

Debug FormatNumber1(-12345) ; -12,345    (using format - own settings)
Debug FormatNumber2(-12345) ; -12 345,00 (no format, it use locale settings - here french)

Posted: Wed Jan 24, 2007 9:29 pm
by AND51
Currently, you're setting decimal point (.) and separator (,) manually. That's the problem: This should be done automatically to prevent mistakes caused by different conventions.
Germans use "," as decimal point, the rest of the world uses ".". It's the same thing for separators but swapped: Germany = ".", the others ","

I can't do this automatically when working with the structure.

Posted: Thu Jan 25, 2007 8:51 am
by Shardik
AND51 wrote: How to convert a integer-number (e. g. 12345) to a grouped integer WITHOUT decimals after the comma?

I always get 12.345,00; but I don't want ,00 ...
AND51 wrote: Currently, you're setting decimal point (.) and separator (,) manually. That's the problem: This should be done automatically to prevent mistakes caused by different conventions.
Germans use "," as decimal point, the rest of the world uses ".". It's the same thing for separators but swapped: Germany = ".", the others ","

I can't do this automatically when working with the structure.
@AND51

Why didn't you try my above posted modified example of Paul's API solution? :roll:
Shardik wrote: It eliminates the need to define a complete NUMBERFMT structure only to get rid of the trailing two decimal places and the point/comma by using this API call:
I had the same problem and have it solved already... :wink:

Posted: Thu Jan 25, 2007 12:48 pm
by AND51
Oh, thank you very much.

But what about the SetLocale-Line? I left it out and it also works!
Actually, I want a procedure containing everything which is needed to convert my numbers; I don't want any calls outside this procedure.