Grouping numbers: 1234 => 12,345

Share your advanced PureBasic knowledge/code with the community.
User avatar
Flype
Addict
Addict
Posts: 1542
Joined: Tue Jul 22, 2003 5:02 pm
Location: In a long distant galaxy

Post by Flype »

hmm, it will not be an easy step... i'll try.
No programming language is perfect. There is not even a single best language.
There are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer
User avatar
Flype
Addict
Addict
Posts: 1542
Joined: Tue Jul 22, 2003 5:02 pm
Location: In a long distant galaxy

Post 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
No programming language is perfect. There is not even a single best language.
There are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer
User avatar
Paul
PureBasic Expert
PureBasic Expert
Posts: 1285
Joined: Fri Apr 25, 2003 4:34 pm
Location: Canada
Contact:

Post 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)
Image Image
User avatar
Flype
Addict
Addict
Posts: 1542
Joined: Tue Jul 22, 2003 5:02 pm
Location: In a long distant galaxy

Post 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.
Last edited by Flype on Thu Jan 18, 2007 9:24 am, edited 1 time in total.
No programming language is perfect. There is not even a single best language.
There are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer
AND51
Addict
Addict
Posts: 1040
Joined: Sun Oct 15, 2006 8:56 pm
Location: Germany
Contact:

Post 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
PB 4.30

Code: Select all

onErrorGoto(?Fred)
User avatar
Flype
Addict
Addict
Posts: 1542
Joined: Tue Jul 22, 2003 5:02 pm
Location: In a long distant galaxy

Post 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...
No programming language is perfect. There is not even a single best language.
There are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer
User avatar
Flype
Addict
Addict
Posts: 1542
Joined: Tue Jul 22, 2003 5:02 pm
Location: In a long distant galaxy

Post by Flype »

corrected :wink: (10 lines).
No programming language is perfect. There is not even a single best language.
There are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer
AND51
Addict
Addict
Posts: 1040
Joined: Sun Oct 15, 2006 8:56 pm
Location: Germany
Contact:

Post 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
PB 4.30

Code: Select all

onErrorGoto(?Fred)
User avatar
Flype
Addict
Addict
Posts: 1542
Joined: Tue Jul 22, 2003 5:02 pm
Location: In a long distant galaxy

Post 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...
No programming language is perfect. There is not even a single best language.
There are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer
User avatar
Shardik
Addict
Addict
Posts: 2060
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Post 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.
AND51
Addict
Addict
Posts: 1040
Joined: Sun Oct 15, 2006 8:56 pm
Location: Germany
Contact:

Post 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 ...
PB 4.30

Code: Select all

onErrorGoto(?Fred)
User avatar
Flype
Addict
Addict
Posts: 1542
Joined: Tue Jul 22, 2003 5:02 pm
Location: In a long distant galaxy

Post 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)
No programming language is perfect. There is not even a single best language.
There are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer
AND51
Addict
Addict
Posts: 1040
Joined: Sun Oct 15, 2006 8:56 pm
Location: Germany
Contact:

Post 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.
PB 4.30

Code: Select all

onErrorGoto(?Fred)
User avatar
Shardik
Addict
Addict
Posts: 2060
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Post 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:
AND51
Addict
Addict
Posts: 1040
Joined: Sun Oct 15, 2006 8:56 pm
Location: Germany
Contact:

Post 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.
PB 4.30

Code: Select all

onErrorGoto(?Fred)
Post Reply