* * *

Author Topic: simple problem: count digits of number  (Read 6447 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: 3461
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: 3114
    • 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: 73
  • 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: 3114
    • 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: 69
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: 3114
    • 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: 2109
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: 2779
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: 6890
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.
Ada's daddy wrote this:"Fools are my theme, let satire be my song."

 

Recent

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