Recent

Author Topic: Issue with FormatFloat  (Read 12401 times)

sfeinst

  • Full Member
  • ***
  • Posts: 230
Issue with FormatFloat
« on: October 21, 2016, 09:00:19 pm »
I'm trying to determine if I am doing something wrong or if there is an issue with FormatFloat.
I am using:
Lazarus 1.6
FreePascal 3.0.0
SVN revision 51630
Windows 10
64-bit

Using the following code:
Code: Pascal  [Select][+][-]
  1. var
  2.   Temp: string;
  3. begin
  4.   Temp := FormatFloat('###,##0.00', 1000);
  5.   Temp := Temp + LineEnding + FormatFloat('###,##0.00', 10000);
  6.   Temp := Temp + LineEnding + FormatFloat('###,##0.00', 10000000 / 1024);
  7.   Temp := Temp + LineEnding + FormatFloat('###,##0.00', 10000000000 / 1024);
  8.   Temp := Temp + LineEnding + FormatFloat('###,##0.00', 10000000000 / (1024*1024));
  9.   Temp := Temp + LineEnding + FormatFloat('###,##0.00', 100000000000 / (1024*1024));
  10.   ShowMessage(Temp);
  11. end;
  12.  

The values look correct, but the location of the comma is off on most of them (or does not exist when it should).

Phil

  • Hero Member
  • *****
  • Posts: 2737
Re: Issue with FormatFloat
« Reply #1 on: October 21, 2016, 09:11:26 pm »
The values look correct, but the location of the comma is off on most of them (or does not exist when it should).

The comma and period in the format string are just placeholders. See FPC documentation:

http://www.freepascal.org/docs-html/rtl/sysutils/formatfloat.html

Maybe use the overloaded form and pass a TFormatSettings record to force the use of specific format settings.



totya

  • Hero Member
  • *****
  • Posts: 720
Re: Issue with FormatFloat
« Reply #2 on: October 21, 2016, 09:28:14 pm »
I'm trying to determine if I am doing something wrong or if there is an issue with FormatFloat.
...
The values look correct, but the location of the comma is off on most of them (or does not exist when it should).

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Memo1: TMemo;
  16.     procedure FormCreate(Sender: TObject);
  17.   private
  18.     { private declarations }
  19.   public
  20.     { public declarations }
  21.   end;
  22.  
  23. var
  24.   Form1: TForm1;
  25.  
  26. implementation
  27.  
  28. {$R *.lfm}
  29.  
  30. { TForm1 }
  31.  
  32.  
  33. procedure TForm1.FormCreate(Sender: TObject);
  34. var
  35.   Temp: string;
  36. begin
  37.   Temp := FormatFloat('###,##0.00', 1000);
  38.   Temp := Temp + LineEnding + FormatFloat('###,##0.00', 10000);
  39.   Temp := Temp + LineEnding + FormatFloat('###,##0.00', 10000000 / 1024);
  40.   Temp := Temp + LineEnding + FormatFloat('###,##0.00', 10000000000 / 1024);
  41.   Temp := Temp + LineEnding + FormatFloat('###,##0.00', 10000000000 / (1024*1024));
  42.   Temp := Temp + LineEnding + FormatFloat('###,##0.00', 100000000000 / (1024*1024));
  43.  
  44.   Temp := Temp + LineEnding;
  45.  
  46.   Temp := Temp + LineEnding + FormatFloat(',0.00', 1000);
  47.   Temp := Temp + LineEnding + FormatFloat(',0.00', 10000);
  48.   Temp := Temp + LineEnding + FormatFloat(',0.00', 10000000 / 1024);
  49.   Temp := Temp + LineEnding + FormatFloat(',0.00', 10000000000 / 1024);
  50.   Temp := Temp + LineEnding + FormatFloat(',0.00', 10000000000 / (1024*1024));
  51.   Temp := Temp + LineEnding + FormatFloat(',0.00', 100000000000 / (1024*1024));
  52.  
  53.   Memo1.Lines.Add(Temp);
  54. end;
  55.  
  56. end.
  57.  

sfeinst

  • Full Member
  • ***
  • Posts: 230
Re: Issue with FormatFloat
« Reply #3 on: October 21, 2016, 09:38:29 pm »
@Phil, I'm not sure what you are saying about them being placeholders.  The link you put in states the decimal point will be placed at that location (which it is) and if a comma is in the string, then a thousands separator will be used.  But it is not separating it by thousands.  The overloaded mthod looks like an object or record you can use to specify characters to use, not locations.

Thanks @Totya.  Removing the # worked.

Was I using the # character incorrectly?

totya

  • Hero Member
  • *****
  • Posts: 720
Re: Issue with FormatFloat
« Reply #4 on: October 21, 2016, 09:59:30 pm »
Was I using the # character incorrectly?

It seems to mee... yes :)

',' is the automatic thousand operator. If you override these auto thousand positions with the '#', the result are mixed garbage, as you see.

These codes results are the same: (# is unnecessary in this case)

Code: Pascal  [Select][+][-]
  1. Temp := Temp + LineEnding;
  2.  
  3.   Temp := Temp + LineEnding + FormatFloat(',0.00', 1000);
  4.   Temp := Temp + LineEnding + FormatFloat(',0.00', 10000);
  5.   Temp := Temp + LineEnding + FormatFloat(',0.00', 10000000 / 1024);
  6.   Temp := Temp + LineEnding + FormatFloat(',0.00', 10000000000 / 1024);
  7.   Temp := Temp + LineEnding + FormatFloat(',0.00', 10000000000 / (1024*1024));
  8.   Temp := Temp + LineEnding + FormatFloat(',0.00', 100000000000 / (1024*1024));
  9.  
  10.   Temp := Temp + LineEnding;
  11.  
  12.   Temp := Temp + LineEnding + FormatFloat('#,##0.00', 1000);
  13.   Temp := Temp + LineEnding + FormatFloat('#,##0.00', 10000);
  14.   Temp := Temp + LineEnding + FormatFloat('#,##0.00', 10000000 / 1024);
  15.   Temp := Temp + LineEnding + FormatFloat('#,##0.00', 10000000000 / 1024);
  16.   Temp := Temp + LineEnding + FormatFloat('#,##0.00', 10000000000 / (1024*1024));
  17.   Temp := Temp + LineEnding + FormatFloat('#,##0.00', 100000000000 / (1024*1024));  

http://www.delphibasics.co.uk/RTL.asp?Name=formatfloat

wp

  • Hero Member
  • *****
  • Posts: 11923
Re: Issue with FormatFloat
« Reply #5 on: October 21, 2016, 10:25:31 pm »
The # symbol is an optional digit, i.e. it will be used if the digit appearing at this place is not a trailing (or leading) zero. The symbol '0', on the other hand, will always be replaced by a digit, even if it is a trailing or leading zero.

Example:
  FormatFloat('0.000', 0.12) --> '0.120'  // a '0' is added because the format string dictates 3 decimal places
  FormatFloat(0.###', 0.12) --> '0.12'   // the '0' is not added because the format string has a '#' here.

or more complex:
  FormatFloat('0.00###', 12.3456789) --> '12.34568'
  FormatFloat('0.00###', 12.3) --> '12.30'

The # symbol is neglected if it occurs before the decimal separator. If you want to have thousand separators (i.e. grouping of three digits), it is only important to write a comma (,) in front of the decimal separator symbol (.). Buf for better readability, often * symbols are used here as well

Example
  FormatFloat('0.0', 1234.5678) --> '1234.6'
  FormatFloat('#,##0.0', 1234.5678) --> '1,234.6'
  FormatFloat(',0.0', 1234.5678) --> '1,234.6'
  FormatFloat('0,000.0', 1.23) --> '0,001.2'

Decimal and thousand separator symbols (. and , respectively) are replaced by the characters defined in the FormatSettings - this, in turn, depends on your language settings. If you are in Europe, often the decimalseparator and thousand separator are interchanged.

If you are in such a country, but want to read/write files in which the default separators are used, you must define your local formatsettings and use this as a parameter of the FormatFloat function:
Code: Pascal  [Select][+][-]
  1. var
  2.   fs: TFormatSettings;
  3.   x: Double;
  4. begin
  5.   fs.DecimalSeparator := '.';
  6.   fs.ThousandSeparator := ',';
  7.   WriteLn(FormatFloat('#,##0.0', 1234.5678, fs);
  8.   x := StrToFloat('1.2', fs)

sfeinst

  • Full Member
  • ***
  • Posts: 230
Re: Issue with FormatFloat [Solved]
« Reply #6 on: October 22, 2016, 04:17:32 pm »
I still don't understand how I used # incorrectly as one of wp's examples uses it the same way I did.

But since removing the # fixed my problem, marking it solved.

wp

  • Hero Member
  • *****
  • Posts: 11923
Re: Issue with FormatFloat [Solved]
« Reply #7 on: October 22, 2016, 04:50:29 pm »
I still don't understand how I used # incorrectly as one of wp's examples uses it the same way I did.
You never told us what is the output of FormatFloat that you see. What's your FormatSettings? Run this, and show us the output:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. uses
  4.   SysUtils;
  5.  
  6. begin
  7.   WriteLn('DecimalSeparator: ', FormatSettings.DecimalSeparator);
  8.   WriteLn('ThousandSeparator: ', FormatSettings.ThousandSeparator);
  9.   WriteLn;
  10.   WriteLn(FormatFloat(',0.00', 1000));
  11.   WriteLn(FormatFloat(',0.00', 10000000 / 1024));
  12.   WriteLn(FormatFloat(',0.00', 10000000000 / 1024));
  13.   WriteLn(FormatFloat(',0.00', 10000000000 / (1024*1024)));
  14.   WriteLn(FormatFloat(',0.00', 100000000000 / (1024*1024)));
  15.   ReadLn;
  16. end.

This is my output (German language settings):
Quote
DecimalSeparator: ,
ThousandSeparator: .

1.000,00
9.765,63
9.765.625,00
9.536,74
95.367,43

totya

  • Hero Member
  • *****
  • Posts: 720
Re: Issue with FormatFloat [Solved]
« Reply #8 on: October 22, 2016, 06:39:07 pm »
I still don't understand how I used # incorrectly as one of wp's examples uses it the same way I did.

Your code:
Code: Pascal  [Select][+][-]
  1. '###,##0.00'

and my examples:
Code: Pascal  [Select][+][-]
  1. ',0.00'
Code: Pascal  [Select][+][-]
  1. '#,##0.00'

wp: you are a very good programmer, but you misunderstand this problem now. See my examples from Reply #2 The first half is the sfeinst code, which produces bad result. This isn't decimal/thousand separator problem.
« Last Edit: October 22, 2016, 06:41:17 pm by totya »

wp

  • Hero Member
  • *****
  • Posts: 11923
Re: Issue with FormatFloat [Solved]
« Reply #9 on: October 22, 2016, 07:05:26 pm »
you misunderstand this problem now. See my examples from Reply #2 The first half is the sfeinst code, which produces bad result. This isn't decimal/thousand separator problem.
No idea what you guys are talking about. To me both format strings produce the same result (Laz trunk / fpc 3.0, Laz 1.6 / fpc 3.0) - see screenshot:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. var
  3.   temp: String;
  4. begin
  5.   temp := '';
  6.     Temp := Temp + LineEnding + FormatFloat(',0.00', 1000);
  7.     Temp := Temp + LineEnding + FormatFloat(',0.00', 10000);
  8.     Temp := Temp + LineEnding + FormatFloat(',0.00', 10000000 / 1024);
  9.     Temp := Temp + LineEnding + FormatFloat(',0.00', 10000000000 / 1024);
  10.     Temp := Temp + LineEnding + FormatFloat(',0.00', 10000000000 / (1024*1024));
  11.     Temp := Temp + LineEnding + FormatFloat(',0.00', 100000000000 / (1024*1024));
  12.  
  13.     Temp := Temp + LineEnding;
  14.  
  15.     Temp := Temp + LineEnding + FormatFloat('#,##0.00', 1000);
  16.     Temp := Temp + LineEnding + FormatFloat('#,##0.00', 10000);
  17.     Temp := Temp + LineEnding + FormatFloat('#,##0.00', 10000000 / 1024);
  18.     Temp := Temp + LineEnding + FormatFloat('#,##0.00', 10000000000 / 1024);
  19.     Temp := Temp + LineEnding + FormatFloat('#,##0.00', 10000000000 / (1024*1024));
  20.     Temp := Temp + LineEnding + FormatFloat('#,##0.00', 100000000000 / (1024*1024));
  21.  
  22.   Memo1.Lines.Text := temp;
  23. end;

totya

  • Hero Member
  • *****
  • Posts: 720
Re: Issue with FormatFloat
« Reply #10 on: October 22, 2016, 07:21:33 pm »
wp... read again my sentences in reply #8 which starting it "wp:" :)

Anyway... se this full examples below. The frist block (from sfeinst) produces bad result, the two last block (from me) are okay.

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Memo1: TMemo;
  16.     procedure FormCreate(Sender: TObject);
  17.   private
  18.     { private declarations }
  19.   public
  20.     { public declarations }
  21.   end;
  22.  
  23. var
  24.   Form1: TForm1;
  25.  
  26. implementation
  27.  
  28. {$R *.lfm}
  29.  
  30. { TForm1 }
  31.  
  32.  
  33. procedure TForm1.FormCreate(Sender: TObject);
  34. var
  35.   Temp: string;
  36.   fs: TFormatSettings;
  37. begin
  38.   fs.DecimalSeparator := '.';
  39.   fs.ThousandSeparator := ',';
  40.  
  41.   Memo1.Lines.Clear;
  42.   Memo1.ScrollBars:=ssBoth;
  43.  
  44.   Temp := FormatFloat('###,##0.00', 1000);
  45.   Temp := Temp + LineEnding + FormatFloat('###,##0.00', 10000, fs);
  46.   Temp := Temp + LineEnding + FormatFloat('###,##0.00', 10000000 / 1024, fs);
  47.   Temp := Temp + LineEnding + FormatFloat('###,##0.00', 10000000000 / 1024, fs);
  48.   Temp := Temp + LineEnding + FormatFloat('###,##0.00', 10000000000 / (1024*1024), fs);
  49.   Temp := Temp + LineEnding + FormatFloat('###,##0.00', 100000000000 / (1024*1024), fs);
  50.  
  51.   Temp := Temp + LineEnding;
  52.  
  53.   Temp := Temp + LineEnding + FormatFloat(',0.00', 1000, fs);
  54.   Temp := Temp + LineEnding + FormatFloat(',0.00', 10000, fs);
  55.   Temp := Temp + LineEnding + FormatFloat(',0.00', 10000000 / 1024, fs);
  56.   Temp := Temp + LineEnding + FormatFloat(',0.00', 10000000000 / 1024, fs);
  57.   Temp := Temp + LineEnding + FormatFloat(',0.00', 10000000000 / (1024*1024), fs);
  58.   Temp := Temp + LineEnding + FormatFloat(',0.00', 100000000000 / (1024*1024), fs);
  59.  
  60.   Temp := Temp + LineEnding;
  61.  
  62.   Temp := Temp + LineEnding + FormatFloat('#,##0.00', 1000, fs);
  63.   Temp := Temp + LineEnding + FormatFloat('#,##0.00', 10000, fs);
  64.   Temp := Temp + LineEnding + FormatFloat('#,##0.00', 10000000 / 1024, fs);
  65.   Temp := Temp + LineEnding + FormatFloat('#,##0.00', 10000000000 / 1024, fs);
  66.   Temp := Temp + LineEnding + FormatFloat('#,##0.00', 10000000000 / (1024*1024), fs);
  67.   Temp := Temp + LineEnding + FormatFloat('#,##0.00', 100000000000 / (1024*1024), fs);
  68.  
  69.   Memo1.Lines.Add(Temp);
  70. end;
  71.  
  72. end.
  73.  

Results are:

Quote
1000,00
1,0000.00
9765.63
9,765,625.00
9536.74
9,5367.43

1,000.00
10,000.00
9,765.63
9,765,625.00
9,536.74
95,367.43

1,000.00
10,000.00
9,765.63
9,765,625.00
9,536.74
95,367.43
« Last Edit: October 22, 2016, 07:28:34 pm by totya »

wp

  • Hero Member
  • *****
  • Posts: 11923
Re: Issue with FormatFloat
« Reply #11 on: October 22, 2016, 07:54:19 pm »
ok - I see it now. Why didn't you add a screenshot or output in the first place? It is very difficult to understand these cryptic responses...

So, what I learned from this excercise is that the count of # in front of the decimal separator DOES matter. And also there must be only one thousand separator - sometimes people use format strings like '#,###,##0.00' because they want millions to be handled with thousand separators as well - but this does not show a thousand separator at all if the number is < 1 million.

Code: Pascal  [Select][+][-]
  1.   WriteLn(FormatFloat('#,###,##0.00', 1000));     // --> '1000'
  2.   WriteLn(FormatFloat('#,###,##0.00', 1000000));  // --> '1,000,000'

Almost looks like a bug to me.

totya

  • Hero Member
  • *****
  • Posts: 720
Re: Issue with FormatFloat
« Reply #12 on: October 22, 2016, 08:09:41 pm »
My opinion this isn't a bug. Because as I wrote above, "," is the automatic thousand separator, and need only one. Someone try these under Delphi... :)

See this example, what is the different between automatic, and manual separtor:

Code: Pascal  [Select][+][-]
  1.  Temp := Temp + LineEnding + FormatFloat(',0.00', 1000*1000, fs);
  2.   Temp := Temp + LineEnding + FormatFloat('#,#,#,0.00', 1000*1000, fs);
  3.   Temp := Temp + LineEnding + FormatFloat('#-#-#-0.00', 1000*1000, fs);        
  4.  

The result for you:

Quote
1,000,000.00
1,000,000.00
1000-0-0-0.00

Phil

  • Hero Member
  • *****
  • Posts: 2737
Re: Issue with FormatFloat
« Reply #13 on: October 22, 2016, 08:42:15 pm »
ok - I see it now. Why didn't you add a screenshot or output in the first place? It is very difficult to understand these cryptic but this does not show a thousand separator at all if the number is < 1 million.

Same result even if only one thousand separator is present:

  WriteLn(FormatFloat('####,##0.00', 1000));     // 1000

It looks like this function is conforming to the old rule of omitting the separator when writing a number if it's less than 10,000.

I've never used FormatFloat and frankly found the FPC documentation language a little confusing. Probably could use a bit of editing.

Maybe best to stick with more standard functions:

  WriteLn(Format('%.2n', [1000.0]));  // must be float  // 1,000.00

  WriteLn(FloatToStrF(1000, ffNumber, 20, 2));  // 1,000.00

Or just write your own function so you get the exact result you want.

totya

  • Hero Member
  • *****
  • Posts: 720
Re: Issue with FormatFloat
« Reply #14 on: October 22, 2016, 09:06:16 pm »
WriteLn(FormatFloat('####,##0.00', 1000));     // 1000
It looks like this function is conforming to the old rule of omitting the separator when writing a number if it's less than 10,000.

No, this "problem" appear too when the number is higher than 10000, see the topic starter example:

Code: Pascal  [Select][+][-]
  1. FormatFloat('###,##0.00', 95367);

Quote
9,5367,00

I think the ',' is not a seperator, this is a command... :)

I've never used FormatFloat and frankly found the FPC documentation language a little confusing. Probably could use a bit of editing.

Yes, I read it, I don't know what does it say really:

Quote
determines the use of the thousand separator character in the output string. If the format string contains one or more ',' charactes, then thousand separators will be used.

If I use one ',' then  thousand separator will be used
If I use more ',' then  thousand separators will be used

lol.
« Last Edit: October 22, 2016, 09:08:18 pm by totya »

 

TinyPortal © 2005-2018