Recent

Author Topic: Count decimal place  (Read 746 times)

bigeno

  • Full Member
  • ***
  • Posts: 240
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: 6158
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.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

bigeno

  • Full Member
  • ***
  • Posts: 240
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

  • Full Member
  • ***
  • Posts: 217
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 aircrafts

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.  O:-)

Bart

  • Hero Member
  • *****
  • Posts: 3481
    • Bart en Mariska's Webstek
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.  O:-)

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

  • Full Member
  • ***
  • Posts: 240
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

  • Full Member
  • ***
  • Posts: 194
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: 1396
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);
  6.   Readln;
  7. end.
On Windows x64 gives the result:
Code: [Select]
1.2000000000000000000 frac:0.1999999999999999600

wp

  • Hero Member
  • *****
  • Posts: 6158
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. ...
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

ASerge

  • Hero Member
  • *****
  • Posts: 1396
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: 6158
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.  
  32.   ReadLn;
  33. end.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

ASerge

  • Hero Member
  • *****
  • Posts: 1396
Re: Count decimal place
« Reply #13 on: May 24, 2019, 11:07:31 pm »
This code is working correctly for me:
My apologies, you're right. The algorithm multiplies the original number, not the fractional part, so that rounding does not accumulate and the result is correct.