Recent

Author Topic: Inconsistent FloatToStrF and Format ?  (Read 1558 times)

comingnine

  • New member
  • *
  • Posts: 25
Inconsistent FloatToStrF and Format ?
« on: September 07, 2018, 10:33:09 am »
The code tries to output a rounded float, and the output are given in the block comment. As seen, FloatToStrF and Format are inconsistent within FPC, and inconsistent with Delphi under Win32 and Lnx64. Could you help to comment how to make FloatToStrF and Format consistent within FPC (and with Delphi) ?

Code: [Select]
{$IFDEF FPC}{$MODE DELPHI}{$ENDIF}
{$IFOPT D+} {$DEFINE DEBUG} {$ENDIF}
{$ASSERTIONS ON}
program TruncBug;
{$IFNDEF FPC}{$APPTYPE CONSOLE}{$ENDIF}
uses SysUtils;

var
  A: Extended;
begin
  A := 0.6659963050;

  (*        FPC 3.1.1-r39514     Delphi7      Delphi25Tokyo
     Win32:   0.66599630       0.66599631      0.66599631
     Win64:   0.66599631                       0.66599631
     Lnx64:   0.66599630
  *)
  Writeln(FloatToStrF(A, ffFixed, 20, 8));

  (*        FPC 3.1.1-r39514     Delphi7      Delphi25Tokyo
     Win32:   0.66599630       0.66599631      0.66599631
     Win64:   0.66599631                       0.66599631
     Lnx64:   0.66599630
  *)
  Writeln(Format('%.8n',[A]));
end.
« Last Edit: September 07, 2018, 10:45:33 am by comingnine »

comingnine

  • New member
  • *
  • Posts: 25
Re: Inconsistent FloatToStrF and Format ?
« Reply #1 on: September 08, 2018, 05:59:57 am »
It seems that the FloatToStrF and Format behave inconsistently even within FPC Win32...

Code: [Select]
{$IFDEF FPC}{$MODE DELPHI}{$ENDIF}
{$IFOPT D+} {$DEFINE DEBUG} {$ENDIF}
{$ASSERTIONS ON}
program TruncBug;
{$IFNDEF FPC}{$APPTYPE CONSOLE}{$ENDIF}
uses
  SysUtils;

var
  A,B: Extended;
begin
  A := 0.3050;
  B := 0.6659963050;

  (*          FPC 3.1.1-r39514     Delphi7              Delphi25Tokyo
     Win32:   0.31; 0.66599630     0.31; 0.66599631     0.31; 0.66599631
     Win64:   0.31; 0.66599631                          0.31; 0.66599631
     Lnx64:   0.31; 0.66599630
  *)
  Writeln(FloatToStrF(A, ffFixed, 20, 2) + '; '+ FloatToStrF(B, ffFixed, 20, 8));

  (*          FPC 3.1.1-r39514     Delphi7              Delphi25Tokyo
     Win32:   0.31; 0.66599630     0.31; 0.66599631     0.31; 0.66599631
     Win64:   0.31; 0.66599631                          0.31; 0.66599631
     Lnx64:   0.31; 0.66599630
  *)
  Writeln(Format('%.2n; %.8n',[A,B]));
end.                                               

Thaddy

  • Hero Member
  • *****
  • Posts: 7195
Re: Inconsistent FloatToStrF and Format ?
« Reply #2 on: September 08, 2018, 08:01:19 am »
Can you explain this:
Code: Pascal  [Select]
  1. {$IFOPT D+} {$DEFINE DEBUG} {$ENDIF}
???  :P ::) O:-) <grumpy  >:D >:D>
{$IFDEF D+} is shorthand for {$IF DEFINED(DEBUG}} or even {$IFDEF DEBUG} so you just wasted a second of your life by writing this... ;D

btw the output on linux armhf 32 is:
Code: Bash  [Select]
  1. 0.31; 0.66599631
  2. 0.31; 0.66599631

Note you are using a quite old trunk and recently there have been some changes regarding this.
IF you use trunk, then ALWAYS use the CURRENT trunk, give or take one or two minor revisions.
Otherwise it is rather pointless. I used 39712.
« Last Edit: September 08, 2018, 08:41:14 am by Thaddy »
inline variables like in D10.3 are a bit like Brexit: if you are given the wrong information it sounds like a good idea. Every kid loves candy, but it makes you fat and your teeth will disappear.

comingnine

  • New member
  • *
  • Posts: 25
Re: Inconsistent FloatToStrF and Format ?
« Reply #3 on: September 08, 2018, 11:21:01 am »
Can you explain this:
Code: Pascal  [Select]
  1. {$IFOPT D+} {$DEFINE DEBUG} {$ENDIF}
???  :P ::) O:-) <grumpy  >:D >:D>
{$IFDEF D+} is shorthand for {$IF DEFINED(DEBUG}} or even {$IFDEF DEBUG} so you just wasted a second of your life by writing this... ;D

Code: [Select]
{$IFDEF FPC}{$MODE DELPHI}{$ENDIF}
{$IFOPT D+} {$DEFINE DEBUG} {$ENDIF} // With this redundant line commented out, nothing will be written. :D
{$ASSERTIONS ON}
program Test;
{$IFNDEF FPC}{$APPTYPE CONSOLE}{$ENDIF}
uses
  SysUtils;

begin
  {$IFDEF DEBUG}
  Writeln('Hello World');
  {$ENDIF}
end.

With that redundant line commented out, nothing will be written. :D

Note you are using a quite old trunk and recently there have been some changes regarding this.
IF you use trunk, then ALWAYS use the CURRENT trunk, give or take one or two minor revisions.
Otherwise it is rather pointless. I used 39712.

I just updated to latest trunk of FPC (3.3.1-r39711) and the same inconsistent results within FPC have been obtained...
« Last Edit: September 08, 2018, 11:33:14 am by comingnine »

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 6627
Re: Inconsistent FloatToStrF and Format ?
« Reply #4 on: September 08, 2018, 12:40:41 pm »
It is strange that linux64 and win64 differ. Of course the extended type is not the most ideal type to test, specially if you include 64-bit

comingnine

  • New member
  • *
  • Posts: 25
Re: Inconsistent FloatToStrF and Format ?
« Reply #5 on: September 08, 2018, 02:25:42 pm »
It is strange that linux64 and win64 differ. Of course the extended type is not the most ideal type to test, specially if you include 64-bit

The output for Double is attached. I am sorry that I do not know how to correctly interpret the output.

Code: [Select]
{$IFDEF FPC}{$MODE DELPHI}{$ENDIF}
{$IFOPT D+} {$DEFINE DEBUG} {$ENDIF}
{$ASSERTIONS ON}
program TruncBug;
{$IFNDEF FPC}{$APPTYPE CONSOLE}{$ENDIF}
uses
  SysUtils;

var
  A,B: Double;
begin
  A := 0.3050;
  B := 0.6659963050;

  (*          FPC 3.1.1-r39514     Delphi7              Delphi25Tokyo
     Win32:   0.31; 0.66599631     0.30; 0.66599630     0.30; 0.66599630
     Win64:   0.31; 0.66599631                          0.31; 0.66599631
     Lnx64:   0.31; 0.66599631
  *)
  Writeln(FloatToStrF(A, ffFixed, 20, 2) + '; '+ FloatToStrF(B, ffFixed, 20, 8));

  (*          FPC 3.1.1-r39514     Delphi7              Delphi25Tokyo
     Win32:   0.30; 0.66599630     0.30; 0.66599630     0.30; 0.66599630
     Win64:   0.31; 0.66599631                          0.31; 0.66599631
     Lnx64:   0.30; 0.66599630
  *)
  Writeln(Format('%.2n; %.8n',[A,B]));
end.


marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 6627
Re: Inconsistent FloatToStrF and Format ?
« Reply #6 on: September 08, 2018, 03:01:02 pm »
I think this is all basic floating point behaviour.

There really is no "good" way. It all depends on the number of operations and the order you do it.

Thaddy

  • Hero Member
  • *****
  • Posts: 7195
Re: Inconsistent FloatToStrF and Format ?
« Reply #7 on: September 08, 2018, 03:39:23 pm »
With that redundant line commented out, nothing will be written. :D
{$IFOPT D+} is enough. That's the point. DEBUG is predefined for the compiler. E.g:
Code: Pascal  [Select]
  1. begin
  2.   {$IFOPT D+} // is the same as { $IFDEF DEBUG} you dont't need to mix them like you did.
  3.  Writeln('Hello World');
  4.  {$ENDIF}
  5. end.
« Last Edit: September 08, 2018, 04:13:59 pm by Thaddy »
inline variables like in D10.3 are a bit like Brexit: if you are given the wrong information it sounds like a good idea. Every kid loves candy, but it makes you fat and your teeth will disappear.

engkin

  • Hero Member
  • *****
  • Posts: 2135
Re: Inconsistent FloatToStrF and Format ?
« Reply #8 on: September 08, 2018, 04:11:37 pm »
When you write:
Code: Pascal  [Select]
  1.   A := 0.3050;
  2.   B := 0.6659963050;

The compiler has to decide the nearest "binary" representations for these "text" decimal numbers.

What do you get if you try:
Code: Pascal  [Select]
  1. var
  2.   A,B: Double;
  3.   Ax: DWord absolute A;
  4.   Bx: DWord absolute B;
  5. begin
  6.   A := 0.3050;
  7.   B := 0.6659963050;
  8.   WriteLn('$',IntToHex(Ax,8),', $',IntToHex(Bx,8));

Gammatester

  • Jr. Member
  • **
  • Posts: 69
Re: Inconsistent FloatToStrF and Format ?
« Reply #9 on: September 08, 2018, 04:15:27 pm »
With that redundant line commented out, nothing will be written. :D
{$IFOPT D+} is enough. That's the point. DEBUG is predefined for the compiler. E.g:
Code: Pascal  [Select]
  1. begin
  2.   {$IFOPT D+} // is the same as {$IFDEF DEBUG} you dont't need to mix them like you did.
  3.  Writeln('Hello World');
  4.  {$ENDIF}
  5. end.
I can confirm comingnine's observation. Here there is no output (Win7/64 and FPC304 or FPC311). May be this is default for Lazarus?

PascalDragon

  • Full Member
  • ***
  • Posts: 206
  • Compiler Developer
Re: Inconsistent FloatToStrF and Format ?
« Reply #10 on: September 08, 2018, 04:22:36 pm »
It is strange that linux64 and win64 differ. Of course the extended type is not the most ideal type to test, specially if you include 64-bit
No, it's not. Win64 is the only x86_64 target where Extended is not available. The other targets have not deprecated the FPU and thus FPC is happily using it for calculations.
The code tries to output a rounded float, and the output are given in the block comment. As seen, FloatToStrF and Format are inconsistent within FPC, and inconsistent with Delphi under Win32 and Lnx64. Could you help to comment how to make FloatToStrF and Format consistent within FPC (and with Delphi) ?
The difference between the FPC targets is that Extended is not available on x86_64-win64, while it is available for the other x86_64 targets as well as all i386 targets (plus i8086, but that's probably neither here nor there for you  :P ). It's also not supported on any other platform we have (e.g. ARM, PowerPC, etc.).
So your best bet would be to stick with Double if you want to be cross platform.
Why Delphi differs to FPC I don't know though it could be (as only the final digit differs) that FPC rounds a tad different than Delphi.
With that redundant line commented out, nothing will be written. :D
{$IFOPT D+} is enough. That's the point. DEBUG is predefined for the compiler. E.g:
Code: Pascal  [Select]
  1. begin
  2.   {$IFOPT D+} // is the same as { $IFDEF DEBUG} you dont't need to mix them like you did.
  3.  Writeln('Hello World');
  4.  {$ENDIF}
  5. end.
You are not entirely correct. The D option or DebugInfo is only enabled (outside of using $D+ or $DebugInfo On) when the parameter -g is passed (see here).
However by default the fpc.cfg file contains this:
Code: [Select]
#IFDEF DEBUG
  -gl
  -Crtoi
  #WRITE Compiling Debug Version
#ENDIF
So it's in fact like this: if you compile with -dDEBUG then -g will be set and thus the D option will be as well.

comingnine

  • New member
  • *
  • Posts: 25
Re: Inconsistent FloatToStrF and Format ?
« Reply #11 on: September 08, 2018, 05:20:15 pm »

What do you get if you try:

It seems the representation is the same.
Code: [Select]
{$IFDEF FPC}{$MODE DELPHI}{$ENDIF}
{$IFOPT D+} {$DEFINE DEBUG} {$ENDIF}
{$ASSERTIONS ON}
program TruncBug;
{$IFNDEF FPC}{$APPTYPE CONSOLE}{$ENDIF}
uses
  SysUtils;

var
  A,B: Double;
  Ax: Cardinal absolute A;
  Bx: Cardinal absolute B;
begin
  A := 0.3050;
  B := 0.6659963050;

  (*          FPC 3.1.1-r39514         Delphi7                  Delphi25Tokyo
     Win32:   0.31; 0.66599631         0.30; 0.66599630         0.30; 0.66599630
     Win64:   0.31; 0.66599631                                  0.31; 0.66599631
     Lnx64:   0.31; 0.66599631
  *)
  Writeln(FloatToStrF(A, ffFixed, 20, 2) + '; '+ FloatToStrF(B, ffFixed, 20, 8));

  (*          FPC 3.1.1-r39514         Delphi7                  Delphi25Tokyo
     Win32:   0.30; 0.66599630         0.30; 0.66599630         0.30; 0.66599630
     Win64:   0.31; 0.66599631                                  0.31; 0.66599631
     Lnx64:   0.30; 0.66599630
  *)
  Writeln(Format('%.2n; %.8n',[A,B]));

  (*          FPC                      Delphi7                  Delphi25Tokyo
     WIN32    $B851EB85, $7BA76B3E     $B851EB85, $7BA76B3E     $B851EB85, $7BA76B3E
     WIN64    $B851EB85, $7BA76B3E                              $B851EB85, $7BA76B3E
     LNX64    $B851EB85, $7BA76B3E
  *)
  WriteLn('$',IntToHex(Ax,8),', $',IntToHex(Bx,8))
end.


Many thanks for your helpful comments !

engkin

  • Hero Member
  • *****
  • Posts: 2135
Re: Inconsistent FloatToStrF and Format ?
« Reply #12 on: September 09, 2018, 07:29:15 am »
It seems the representation is the same.
While most likely they are the same, but the previous code is wrong. We should use QWord instead of DWord. Double is 8 bytes.