### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

### Author Topic: simple problem: count digits of number  (Read 24124 times)

#### chickpea

• Newbie
• Posts: 3
##### simple problem: count digits of number
« on: January 30, 2018, 12:02:33 pm »
Hi there,

Here's my approach to counting the digits of a number in pascal.
It does work, but as soon as I enter a number with more than 5 digits the program returns a 5.
I don't get why. If anyone could give me a hint that'd be great.
Comments and program names in German, but I suppose you get the idea.

program ZifferAnzahl (input, output);

type
tNatZahl = 0..maxint;

var
inZahl : tNatZahl;

function countZiff (inZahl : tNatZahl) : tNatZahl;

{ gibt Anzahl der Ziffern von inZahl zurück }

var
i,
j : tNatZahl;

begin

{ initialisierung }

i := inZahl;
j := 0;

{ inZahl so oft durch 10 Teilen, bis 0 erreicht wird }

while i > 0 do
begin
i := i div 10;
j := j + 1
end;

countZiff := j
end; { countZiff }

begin
writeln ('Bitte Zahl eingeben');
writeln(countZiff(inZahl))
end. { ZifferAnzahl }

#### rvk

• Hero Member
• Posts: 4480
##### Re: simple problem: count digits of number
« Reply #1 on: January 30, 2018, 12:19:07 pm »
You are probably in TP mode. {\$mode TP}
There maxint is a 2 byte integer.
Which is maximum 32767 (see, 5 digits)

Add {\$mode objfpc} just below program and your Integer will be 4 bytes.

B.T.W. you can check this by adding the lines
Code: Pascal  [Select][+][-]
1. writeln(sizeof(inZahl));
2. writeln(maxint);

Of course you can also do this instead of changing mode:
Code: Pascal  [Select][+][-]
1. type
2.   tNatZahl = 0..99999999;
« Last Edit: January 30, 2018, 12:21:54 pm by rvk »

#### chickpea

• Newbie
• Posts: 3
##### Re: simple problem: count digits of number
« Reply #2 on: January 30, 2018, 04:05:34 pm »
Great, that was the error indeed. Thanks for explaining.

Working correctly when change to

tNatZahl = 0..999999999;

Cheers

#### Bart

• Hero Member
• Posts: 4415
##### Re: simple problem: count digits of number
« Reply #3 on: January 30, 2018, 09:08:30 pm »
Perhaps: tNatZahl = QWord;

Another dummy approach
Code: Pascal  [Select][+][-]
1. uses sysutils;
2.
3. function countZiff (inZahl : tNatZahl) : tNatZahl;
4. begin
5.   Result := Length(IntToStr(inZahl));
6. end;
7.

or
Code: Pascal  [Select][+][-]
1. uses Math;
2.
3. function countZiff (inZahl : tNatZahl) : tNatZahl;
4. begin
5.   Result := Floor(Log10(inZahl));
6. end;
7.

Bart
« Last Edit: January 30, 2018, 09:32:20 pm by Bart »

#### furious programming

• Hero Member
• Posts: 513
• I click a little.
##### Re: simple problem: count digits of number
« Reply #4 on: February 05, 2018, 05:43:04 am »
@chickpea: you don't need a custom function, just use some helpers from SysUtils unit:

Code: Pascal  [Select][+][-]
1. Count := MyIntVar.ToString.Length;

Lazarus 2.0.12 with FPC 3.2.0 (SVN Revision 64642), Windows 10 — all 64-bit

#### Bart

• Hero Member
• Posts: 4415
##### Re: simple problem: count digits of number
« Reply #5 on: February 05, 2018, 01:45:10 pm »
Code: Pascal  [Select][+][-]
1. Count := MyIntVar.ToString.Length;

That's basically the same as I wrote above (Length(IntToStr(InZahl))), proabbly a bit less efficient (unless all this code for the helper is inlined), albeit more fancy.
The Log10 approach might actually be faster?

Bart

#### isidroco

• New Member
• Posts: 12
##### Re: simple problem: count digits of number
« Reply #6 on: February 28, 2018, 07:43:43 pm »
Floating point Log will always be slower, it involves series calculation even when done in mathcoprocessor will be slow, and sending/getting results from coprocessor will probably be slower than integer operations unless number of digits is very big (>50) in which log10 might be faster.

PS: In my old days when clocking stuff in 80286 cpus, I found out in TP5.0 that x*0.33333333 was a lot faster than x/3. (x: real) (And a lot was about 3 times faster). So it's better to multiply by a constant than divide by it's inverse.

#### A.S.

• Jr. Member
• Posts: 76
##### Re: simple problem: count digits of number
« Reply #7 on: February 28, 2018, 08:25:10 pm »
Function "countZiff" is incorrect: it returns 0 if inZahl=0, but "0" contains the single digit zero, so countZiff(0) must return 1.

#### Bart

• Hero Member
• Posts: 4415
##### Re: simple problem: count digits of number
« Reply #8 on: February 28, 2018, 10:30:37 pm »
Floating point Log will always be slower, it involves series calculation even when done in mathcoprocessor will be slow, and sending/getting results from coprocessor will probably be slower than integer operations unless number of digits is very big (>50) in which log10 might be faster.

The IntToStr part also costs time.

Code: Pascal  [Select][+][-]
1. program Project1;
2. {\$mode objfpc}{\$h+}
3.
4. uses SysUtils, Math;
5.
6. function cntIntToStr(Z: Integer): Integer;
7. begin
8.   Result := Length(IntToStr(Z));
9. end;
10.
11. function cntTypeHelper(Z: Integer): Integer;
12. begin
13.   Result := Z.ToString.Length;
14. end;
15.
16. function cntLog10(Z: Integer): Integer;
17. begin
18.   if Z = 0 then
19.     Result := 1
20.   else
21.     Result := 1+Floor(Log10(Z));
22. end;
23.
24. const
25.   cycle = 1000000;
26. var
27.   i: Integer;
28.   T0, T1: QWord;
29.   Sample, Res: Integer;
30. begin
31.   Sample := MaxInt; //2147483647
32.   T0 := GetTickCount64;
33.   for i := 1 to cycle do Res := cntIntToStr(Sample);
34.   T1 := GetTickCount64;
35.   writeln(format('cntIntToStr  : %.4d ticks (Res = %d)',[T1-T0,Res]));
36.
37.   T0 := GetTickCount64;
38.   for i := 1 to cycle do Res := cntTypeHelper(Sample);
39.   T1 := GetTickCount64;
40.   writeln(format('cntTypeHelper: %.4d ticks (Res = %d)',[T1-T0,Res]));
41.
42.   T0 := GetTickCount64;
43.   for i := 1 to cycle do Res := cntLog10(Sample);
44.   T1 := GetTickCount64;
45.   writeln(format('cntLog10     : %.4d ticks (Res = %d)',[T1-T0,Res]));
46. end.

Outputs:
Code: [Select]
`cntIntToStr  : 1328 ticks (Res = 10)cntTypeHelper: 1359 ticks (Res = 10)cntLog10     : 0047 ticks (Res = 10)`
Notice that using typehelpers this is just a little bit slower than using classic Length(IntToStr()), but the Log10 is way faster.

Bart

#### isidroco

• New Member
• Posts: 12
##### Re: simple problem: count digits of number
« Reply #9 on: March 01, 2018, 02:14:18 am »
Notice that using typehelpers this is just a little bit slower than using classic Length(IntToStr()), but the Log10 is way faster.
Bart

Thanks for the eyeopener, evidently TP5 real type without using coprocessor were a lot slower than today's implementation which uses it. And IntToStr involves a lot of DIV instructions. (In my days 80387 math coprocessor costed 800u\$ so most machines didn't have it...). Actually I had to program log(X+1) and it's inverse using taylor series, and it was quite faster than using internal LOG(x+1) for values of x close to 0. Math coprocessor have internal instructions for that: FYL2XP1 / F2XM1

#### engkin

• Hero Member
• Posts: 2924
##### Re: simple problem: count digits of number
« Reply #10 on: March 01, 2018, 05:12:19 am »
On my old laptop this seems faster:
Code: Pascal  [Select][+][-]
1.   function cntTbl(Z: integer): integer;
2.   begin
3.     if Z <= 9 then
4.       Result := 1
5.     else if Z <= 99 then
6.       Result := 2
7.     else if Z <= 999 then
8.       Result := 3
9.     else if Z <= 9999 then
10.       Result := 4
11.     else if Z <= 99999 then
12.       Result := 5
13.     else if Z <= 999999 then
14.       Result := 6
15.     else if Z <= 9999999 then
16.       Result := 7
17.     else if Z <= 99999999 then
18.       Result := 8
19.     else if Z <= 999999999 then
20.       Result := 9
21.     else if Z <= 9999999999 then
22.       Result := 10
23.     else if Z <= 99999999999 then
24.       Result := 11
25.     else if Z <= 999999999999 then
26.       Result := 12
27.     else if Z <= 9999999999999 then
28.       Result := 13
29.     else if Z <= 99999999999999 then
30.       Result := 14
31.     else if Z <= 999999999999999 then
32.       Result := 15
33.     else if Z <= 9999999999999999 then
34.       Result := 16
35.     else if Z <= 99999999999999999 then
36.       Result := 17
37.     else if Z <= 999999999999999999 then
38.       Result := 18
39.     else if Z <= 9999999999999999999 then
40.       Result := 19;
41.   end;

Did not try a case statement.

#### isidroco

• New Member
• Posts: 12
##### Re: simple problem: count digits of number
« Reply #11 on: March 01, 2018, 02:46:29 pm »
Based on last post this would converge faster:
Code: Pascal  [Select][+][-]
1. function cntTbl(Z: int64): int64;
2. begin
3.   if Z<0 then Z:=abs(Z);
4.   if Z < 10^8 then
5.     if Z < 10^4 then
6.       if Z < 10^2 then
7.         if Z < 10^1 then result:=1 else result:=2
8.       else
9.         if Z < 10^3 then result:=3 else result:=4
10.     else
11.       if Z < 10^6 then
12.         if Z < 10^5 then result:=5 else result:=6
13.       else
14.         if Z < 10^7 then result:=7 else result:=8
15.   else
16.         Return( 8 + cntTbl( z div 10^8) );
17. end;
18.

Which would recurse call if more than 8 digits, and will return digit count of negative numbers. Same code could be used for real values (although in that case, in last statement I would switch to LOG10 instead of recurse calling to quickly get results for high exponents ).
« Last Edit: March 01, 2018, 02:50:22 pm by isidroco »

#### howardpc

• Hero Member
• Posts: 3753
##### Re: simple problem: count digits of number
« Reply #12 on: March 01, 2018, 04:59:41 pm »
Did not try a case statement.

In my tests case is marginally the fastest so far.

Code: Pascal  [Select][+][-]
1. program CompareCountDigits;
2. {\$mode objfpc}{\$h+}
3.
4. uses SysUtils, Math;
5.
6. const
7.   Number: int64 = MaxInt; //2147483647
8.   Iterations: DWord = 1000000;
9.
10. type
11.   TCountFunc = function(anInt: Int64): Int64;
12.
13.   TFuncEnum = (cfIntToStr, cfTbl, cfTbl64, cfCaseTbl, cfTypeHelper, cfLog10);
14.
15.   TFuncRec = record
16.     func: TCountFunc;
17.     name: String;
18.   end;
19.
20.   TFuncRecArray = array[TFuncEnum] of TFuncRec;
21.
22. function GetFName(aFuncEnum: TFuncEnum): String;
23. begin
24.   WriteStr(Result, aFuncEnum);
25.   Delete(Result, 1, 2);
26. end;
27.
28. function DoFunction(func: TCountFunc; out ticks: QWord): Int64;
29. var
30.   i: Integer;
31.   t0: QWord;
32. begin
33.   t0 := GetTickCount64;
34.   for i := 1 to iterations do
35.     Result := func(Number);
36.   ticks := GetTickCount64;
37.   Dec(ticks, t0);
38. end;
39.
40. function cntIntToStr(Z: Int64): Int64;
41. begin
42.   Result := Length(IntToStr(Z));
43. end;
44.
45. function cntTbl(Z: Int64): Int64;
46.   begin
47.     if Z <= 9 then
48.       Result := 1
49.     else if Z <= 99 then
50.       Result := 2
51.     else if Z <= 999 then
52.       Result := 3
53.     else if Z <= 9999 then
54.       Result := 4
55.     else if Z <= 99999 then
56.       Result := 5
57.     else if Z <= 999999 then
58.       Result := 6
59.     else if Z <= 9999999 then
60.       Result := 7
61.     else if Z <= 99999999 then
62.       Result := 8
63.     else if Z <= 999999999 then
64.       Result := 9
65.     else if Z <= 9999999999 then
66.       Result := 10
67.     else if Z <= 99999999999 then
68.       Result := 11
69.     else if Z <= 999999999999 then
70.       Result := 12
71.     else if Z <= 9999999999999 then
72.       Result := 13
73.     else if Z <= 99999999999999 then
74.       Result := 14
75.     else if Z <= 999999999999999 then
76.       Result := 15
77.     else if Z <= 9999999999999999 then
78.       Result := 16
79.     else if Z <= 99999999999999999 then
80.       Result := 17
81.     else if Z <= 999999999999999999 then
82.       Result := 18
83.     else if Z <= 9223372036854775807 then
84.       Result := 19;
85.   end;
86.
87. function cntCaseTbl(Z: Int64): Int64;
88.   begin
89.     case Z of
90.       0..9: Exit(1);
91.       10..99 : Exit(2);
92.       100..999: Exit(3);
93.       1000..9999: Exit(4);
94.       10000..99999: Exit(5);
95.       100000..999999: Exit(6);
96.       1000000..9999999: Exit(7);
97.       10000000..99999999: Exit(;
98.       100000000..999999999: Exit(9);
99.       1000000000..9999999999: Exit(10);
100.       10000000000..99999999999: Exit(11);
101.       100000000000..999999999999: Exit(12);
102.       1000000000000..9999999999999: Exit(13);
103.       10000000000000..99999999999999: Exit(14);
104.       100000000000000..999999999999999: Exit(15);
105.       1000000000000000..9999999999999999: Exit(16);
106.       10000000000000000..99999999999999999: Exit(17);
107.       100000000000000000..999999999999999999: Exit(18);
108.       1000000000000000000..9223372036854775807: Exit(19);
109.     end;
110.   end;
111.
112. function cntTbl64(Z: int64): int64;
113. begin
114.   if Z<0 then Z:=abs(Z);
115.   if Z < 100000000 then
116.     if Z < 10000 then
117.       if Z < 100 then
118.         if Z < 10 then result:=1 else result:=2
119.       else
120.         if Z < 1000 then result:=3 else result:=4
121.     else
122.       if Z < 1000000 then
123.         if Z < 1000000 then result:=5 else result:=6
124.       else
125.         if Z < 10000000 then result:=7 else result:=8
126.   else Exit(8 + cntTbl( z div 100000000) );
127. end;
128.
129. function cntTypeHelper(Z: Int64): Int64;
130. begin
131.   Result := Z.ToString.Length;
132. end;
133.
134. function cntLog10(Z: Int64): Int64;
135. begin
136.   if Z = 0 then
137.     Result := 1
138.   else
139.     Result := 1+Floor(Log10(Z));
140. end;
141.
142. var
143.   fra: TFuncRecArray;
144.   fe: TFuncEnum;
145.   fr: TFuncRec;
146.   res: Int64;
147.   ticks: QWord;
148.
149. begin
150.   fra:=default(TFuncRecArray);
151.   for fe in TFuncEnum do
152.     fra[fe].name := GetFName(fe);
153.
154.   fra[cfIntToStr].func :=  @cntIntToStr;
155.   fra[cfTbl].func :=       @cntTbl;
156.   fra[cfTbl64].func :=     @cntTbl64;
157.   fra[cfCaseTbl].func :=   @cntCaseTbl;
158.   fra[cfTypeHelper].func:= @cntTypeHelper;
159.   fra[cfLog10].func :=     @cntLog10;
160.
161.   for fr in fra do begin
162.     res := DoFunction(fr.func, ticks);
163.     WriteLn(fr.name, ticks:4, ' ticks (result = ', res, ')');
164.   end;
165.
167. end.

Typical output:

IntToStr 523 ticks (result = 10)
Tbl  12 ticks (result = 10)
Tbl64  14 ticks (result = 10)
CaseTbl   9 ticks (result = 10)
TypeHelper 528 ticks (result = 10)
Log10  53 ticks (result = 10)
« Last Edit: March 01, 2018, 05:04:02 pm by howardpc »

#### isidroco

• New Member
• Posts: 12
##### Re: simple problem: count digits of number
« Reply #13 on: March 01, 2018, 05:42:13 pm »
My version is faster if digits are less than 8 (made a 16 digit version but was unnecessary long), if typical cases are higher than 8 digits probably is worthwhile. Also negative numbers should be contempleted in all versions, an ABS() statement must be added to all other versions.
Try comparing with 7 digits numbers and post results, thanks!

• Hero Member
• Posts: 10809
##### Re: simple problem: count digits of number
« Reply #14 on: March 01, 2018, 05:48:23 pm »
Code: Bash  [Select][+][-]
1. IntToStr2334 ticks (result = 10)
2. Tbl 149 ticks (result = 10)
3. Tbl64 237 ticks (result = 10)
4. CaseTbl  96 ticks (result = 10)
5. TypeHelper2398 ticks (result = 10)
6. Log10 464 ticks (result = 10)
On a Raspberry pi 3.