[fpc-pascal] Floating point question

Thomas Kurz fpc.2021 at t-net.ruhr
Tue Feb 6 18:09:59 CET 2024


I think the reason why this new behavior doesn't occur with 1440.1 is that this number cannot be reduced to "single" precision. It will keep "double" precision.

Consider this instead:

program TESTDBL1 ;

var TT : double ; EE: double;

begin (* HAUPTPROGRAMM *)
   TT := 8427 + 33 / 1440.5 ;
   EE := Double(8427) + Double(33) / Double(1440.5);
   WRITELN ( 'tt=' , TT : 20 : 20 ) ;
   WRITELN ( 'ee=' , EE : 20 : 20 ) ;
end (* HAUPTPROGRAMM *) .

Result:

tt=8427.02246093750000000000
ee=8427.02290871225340000000

So it's the same as with ".0". FPC treats the constant as type "single". Imho, this is perfectly legal, but when assigning an expression to a "double" variable, an implicit cast to double should occur.

When using a variable of type "single" (instead of a constant), this casting is done:

program TESTDBL1 ;

{$mode objfpc}

var TT : double ;
    EE: double;
    x: Single = 1440.5;

begin (* HAUPTPROGRAMM *)
   TT := 8427 + 33 / x ;
   EE := 8427 + 33 / Double(x);
   WRITELN ( 'tt=' , TT : 20 : 20 ) ;
   WRITELN ( 'ee=' , EE : 20 : 20 ) ;
end (* HAUPTPROGRAMM *) .

Prints:
tt=8427.02290871225340000000
ee=8427.02290871225340000000

I don't know whether this is intentional or not, but I cannot see any good reason why using a constant in an expression has to be treated differently than using a variable. To me as a programmer, this behavior is unexpected.

If the 2.2 change is not going to be reverted (and as far as I understand Florian correctly, it won't be changed), maybe one could at least introduce a warning about a loss of precision when using a constant of type "single" in an expression which will be assigned to a variable of type "double".

Kind regards,
Thomas




----- Original Message ----- 
From: James Richters via fpc-pascal <fpc-pascal at lists.freepascal.org>
To: 'FPC-Pascal users discussions' <fpc-pascal at lists.freepascal.org>
Sent: Tuesday, February 6, 2024, 16:23:30
Subject: [fpc-pascal] Floating point question

What's apparently happening now is:
MyExtended := ReducePrecisionIfNoLossOfData (8246) +
ReducePrecisionIfNoLossOfData (33.0) / ReducePrecisionIfNoLossOfData
(1440.0);
But it is not being done correctly, the 1440.0 is not being reduced all the
way to an integer, because it was, everything would work.  The 1440.0 is
being considered a single, and the division is also being now considered a
single, even though that is incorrect.   But 1440.1 is not being considered
a single, because 1440.1 is not breaking everything.

What should be happening is:
MyExtended := ReducePrecisionIfNoLossOfData(8246+33.0/1440.0);


I just realized something...  regardless of when or how the reduction in
precision is happening, the bug is different than that,  because the result
of a byte divided by a single when stored in a double is a double, NOT a
single,  there should be no problem here, there is a definite bug. 

Consider this:
program TESTDBL1 ;

Const
   HH = 8427.0229166666666666666666666667;
Var
   AA : Integer;
   BB : Byte;
   CC : Single;
   DD : Single;
   EE : Double;
   FF : Extended;
   GG : Extended;
   

begin
   AA := 8427;
   BB := 33;
   CC := 1440.0;
   DD := AA+BB/CC;
   EE := AA+BB/CC;
   FF := AA+BB/CC;
   GG := 8427+33/1440.0;
   
   WRITELN ( 'DD = ',DD: 20 : 20 ) ;
   WRITELN ( 'EE = ',FF: 20 : 20 ) ;
   WRITELN ( 'FF = ',FF: 20 : 20 ) ;
   WRITELN ( 'GG = ',GG: 20 : 20 ) ;
   WRITELN ( 'HH = ',HH: 20 : 20 ) ;
end.

When I do the division of a byte by a single and store it in an extended, I
get the division carried out as an extended.
FF, GG, and HH should all be exactly the same if there is not a bug.
But:

DD = 8427.02246100000000000000
EE = 8427.02291666666666625000
FF = 8427.02291666666666625000
GG = 8427.02246093750000000000
HH = 8427.02291666666666625000

GG,  the one with constants, is doing it wrong... 

If the entire formula was calculated the original way at full precision,
then only result was reduced if there was no loss in precision right before
storing as a constant,  then this solves the problems for everyone, and this
is the correct way to do this.  Then everyone is happy, no Delphi warnings,
no needlessly complex floating point computations if the result of all the
math is a byte, and no confusion as to why it works with 1440.1 and not
1440.0  Compatibility with all versions of Pascal,  etc..  

This calculation is only done once by the compiler, the calculation should
be done at full possible precision and only the result stored in a reduced
way if it makes sense to do so.

The problem I have with the changes made with v2.2, is that it's obvious
that the change was going to introduce a known bug at the time:
"Effect: some expressions, in particular divisions of integer values by
floating point constants, may now default to a lower precision than in the
past."
How is this acceptable or the default?? 

"Remedy: if more precision is required than the default, typecast the
floating point constant to a higher precision type, e.g. extended(2.0).
Alternatively, you can use the -CF command line option to change the default
precision of all floating point constants in the compiled modules."

The first remedy is unreasonable, I should not have to go through thousands
of lines of code and cast my constants, it was never a requirement of Pascal
to do this. 

Great if -CF80 worked, but even if you are happy with -CF64, my problem is:
how is anyone coming into FPC after 2.2 supposed to know that their
constants that always worked before are going to no longer be accurate??

The better thing to do would be to do it RIGHT before releasing the change
so that it can't be a problem for anyone, and make:
"New behaviour: floating point constants are now considered to be of the
lowest precision which doesn't cause data loss"  a true statement.

If the entire formula was evaluated at full precision, and only the result
was stored as a lower precision if possible, then there is never a problem
for anyone.


James

_______________________________________________
fpc-pascal maillist  -  fpc-pascal at lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal



More information about the fpc-pascal mailing list