### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

### Author Topic: Count decimal place  (Read 1474 times)

#### bigeno

• Sr. Member
• Posts: 250
##### Count decimal place
« on: May 24, 2019, 12:21:40 pm »
What is the best way to count decimal place/precision for number ?
as example
Code: Pascal  [Select][+][-]
1.  x := decplaces(2.23000); //should return 2
2.  x := decplaces(2.2002); //should return 4
3. etc.
4.
for now I think to  multiply by 10 until frac will = 0, but maybe there is better way ?
« Last Edit: May 24, 2019, 12:34:07 pm by bigeno »

#### wp

• Hero Member
• Posts: 7958
##### Re: Count decimal place
« Reply #1 on: May 24, 2019, 12:41:40 pm »
Some effort for "nice" string presentation has been put into the function FloatToStr. Therefore, I would convert the number to string and count the characters following the decimal separator:

Code: Pascal  [Select][+][-]
1.   function CountDecimals(value: Double): Integer;
2.   var
3.     s: String;
4.     p: Integer;
5.   begin
6.     s := FloatToStr(Value);
7.     p := pos(FormatSettings.DecimalSeparator, s);
8.     if p = 0 then
9.       Result := 0
10.     else
11.       Result := Length(s) - p;
12.   end;

However, usually floating point numbers cannot be expressed exactly like this by the computer due to the final resolution of floating point data types. While most of these issues seem to be covered by the FloatToStr function there is still a risk that many more decimals will be seen than what is expected.
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

#### bigeno

• Sr. Member
• Posts: 250
##### Re: Count decimal place
« Reply #2 on: May 24, 2019, 01:00:13 pm »
count the characters ... after all it works in my case
until 13 decimal places works ok, but in my case user can enter max 11 so good in my case thx

#### Zvoni

• Sr. Member
• Posts: 433
##### Re: Count decimal place
« Reply #3 on: May 24, 2019, 03:01:40 pm »
Hrhrhr
1. Convert To String
2. Split along Decimal separator --> The High-End of the resulting String-Array contains your fraction
3. String-Reverse the Fraction
4. Convert to Integer (so any leading Zeros in the reversed Fraction get eliminated --> those were trailing zeros in the original order)
5. Convert back To string
6. Check String-Length
7. Done
« Last Edit: May 24, 2019, 03:03:31 pm by Zvoni »
One System to rule them all, One IDE to find them,
One Code to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
People call me crazy, because i'm jumping out of perfectly fine aircraft

#### engkin

• Hero Member
• Posts: 2513
##### Re: Count decimal place
« Reply #4 on: May 24, 2019, 05:51:56 pm »
Hrhrhr
1. Convert To String
2. Split along Decimal separator --> The High-End of the resulting String-Array contains your fraction
3. String-Reverse the Fraction
4. Convert to Integer (so any leading Zeros in the reversed Fraction get eliminated --> those were trailing zeros in the original order)
5. Convert back To string
6. Check String-Length
7. Done
Did you see wp's code? It was almost like: 1,2 and 6 (he did not need to split, just used pos)

The challenge is to find the answer without converting to string.

#### Bart

• Hero Member
• Posts: 4107
##### Re: Count decimal place
« Reply #5 on: May 24, 2019, 06:39:48 pm »
The challenge is to find the answer without converting to string.

How?
For the compiler 2.0 and 2.0000 the same.
'2.0' and '2.0000' are not.

Bart

#### engkin

• Hero Member
• Posts: 2513
##### Re: Count decimal place
« Reply #6 on: May 24, 2019, 07:26:50 pm »
According to the original post:

decplaces(2.23000); //should return 2

#### bigeno

• Sr. Member
• Posts: 250
##### Re: Count decimal place
« Reply #7 on: May 24, 2019, 07:56:09 pm »
String converting is ok for me.
But my first try was numerical
Code: Pascal  [Select][+][-]
1. function decplaces2(const val: extended): integer;
2. var
3.   i: integer;
4.   s: extended;
5. begin
6.  i := 0;
7.  s := val;
8.  while i < 20 do begin
9.     if IsZero(frac(s)) then exit(i);
10.     s := s * 10;
11.     Inc(i);
12.  end;
13. end;
and that code gave me insane results. Round off errors ?

#### winni

• Hero Member
• Posts: 2121
##### Re: Count decimal place
« Reply #8 on: May 24, 2019, 09:55:24 pm »
The documentation is not quiet clear, but

http://wiki.freepascal.org/Function

says, you must set

Code: Pascal  [Select][+][-]
1. {\$mode objFPC}

to set the exit parameter as function result
.

Winni

#### ASerge

• Hero Member
• Posts: 1721
##### Re: Count decimal place
« Reply #9 on: May 24, 2019, 10:26:04 pm »
Round off errors ?
Representations of floating numbers are not accurate. This simple code:
Code: Pascal  [Select][+][-]
1. {\$APPTYPE CONSOLE}
2. var
3.   R: ValReal = 1.2;
4. begin
5.   Writeln(R:0:19, ' frac:', Frac(R):0:19);
7. end.
On Windows x64 gives the result:
Code: [Select]
`1.2000000000000000000 frac:0.1999999999999999600`

#### wp

• Hero Member
• Posts: 7958
##### Re: Count decimal place
« Reply #10 on: May 24, 2019, 10:31:57 pm »
Code: Pascal  [Select][+][-]
1. function decplaces2(const val: extended): integer;
2. var
3.   i: integer;
4.   s: extended;
5. begin
6.  i := 0;
7.  s := val;
8.  while i < 20 do begin
9.     if IsZero(frac(s)) then exit(i);
10.     s := s * 10;
11.     Inc(i);
12.  end;
13. end;
and that code gave me insane results. Round off errors ?

I don't see a problem in this code. When you look into the sources of IsZero (just CTRL-click on this identifier to open unit Math at its implementation) you'll see that calling this function without a second parameter means application of a tolerance for the zero-check of 1E-16. This means that round-off error below 1E-16 should be handled correctly. If this is too sensitive just use your own epsilon in IsZero:

Code: Pascal  [Select][+][-]
1. const
2.   EPS = 1E-9;
3. ...
4.   if IsZero(frac(s), EPS) then exit(i);
5. ...
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

#### ASerge

• Hero Member
• Posts: 1721
##### Re: Count decimal place
« Reply #11 on: May 24, 2019, 10:43:57 pm »
I don't see a problem in this code.
See my example above. The first call to the Frac function increased the number of digits from 1 to 17. A subsequent call to the Frac function can increase this number to 50. IsZero will help only in really late, i.e. not 50, but 48, for example. Either way, it's a problematic code.

#### wp

• Hero Member
• Posts: 7958
##### Re: Count decimal place
« Reply #12 on: May 24, 2019, 10:49:52 pm »
After the last "real" decimal the frac will be a very close to zero and will be caught by the epsilon of the IsZero function, even for the default epsilon of 1E-16 for extended values.

This code is working correctly for me:
Code: Pascal  [Select][+][-]
1. program Project1;
2.
3. {\$mode objfpc}{\$H+}
4.
5. uses
6.   math;
7.
8. function decplaces2(const val: extended; Epsilon: Extended = 0.0): integer;
9. var
10.   i: integer;
11.   s: extended;
12. begin
13.  i := 0;
14.  s := val;
15.  while i < 20 do begin
16.     if IsZero(frac(s), Epsilon) then exit(i);
17.     s := s * 10;
18.     Inc(i);
19.  end;
20. end;
21.
22. const
23.   EPS = 1E-9;
24. var
25.   a: extended;
26. begin
27.   a := 1.2;
28.   WriteLn('Full presentation of a: ', a);
29.   WriteLn('Decimal places: ', DecPlaces2(a));
30.   WriteLn('Decimal places with EPS: ', DecPlaces2(a, EPS));
31.