* * *

Author Topic: simple problem: count digits of number  (Read 3929 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.

Thanks in advance!




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');
  readln (inZahl);
  writeln(countZiff(inZahl))
end. { ZifferAnzahl }
   

rvk

  • Hero Member
  • *****
  • Posts: 3428
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: 2956
    • Bart en Mariska's Webstek
Re: simple problem: count digits of number
« Reply #3 on: January 30, 2018, 09:08:30 pm »
Perhaps: tNatZahl = QWord;

Another dummy approach  O:-)
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

  • Jr. Member
  • **
  • Posts: 52
  • I click a little.
    • TreeStructInfo - format for text and binary configuration files
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;

It's readable enough.

Bart

  • Hero Member
  • *****
  • Posts: 2956
    • Bart en Mariska's Webstek
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: 66
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: 2956
    • Bart en Mariska's Webstek
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: 1971
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: 2669
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.  
  166.   ReadLn;
  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!

Thaddy

  • Hero Member
  • *****
  • Posts: 5980
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.
I might not give the answer that you want me to.. Peter Green 1969

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus