[fpc-pascal] Floating point question

Bernd Oppolzer bernd.oppolzer at t-online.de
Tue Feb 20 00:15:40 CET 2024


See below ...


Am 19.02.2024 um 02:00 schrieb James Richters via fpc-pascal:
>
> >And if you have set the precision, then the calculation will be 
> identical to the calculation when you use a variable of the same type 
> (if not, it's indeed a bug).
>
> This is what I have been trying to point out.Math with identical 
> casting with variables and constants are not the same.
>
> Maybe if I try with a simpler example:
>
> program Const_Vs_Var;
>
> Const
>
> A_const = Byte(1);
>
> B_const = Single(3.5);
>
> Var
>
> A_Var : Byte;
>
> B_Var : Single;
>
> Const_Ans1, Var_Ans1 : Extended;
>
> Begin
>
> A_Var := A_Const;
>
> B_Var := B_Const;
>
> Const_Ans1 := Extended(Byte(A_Const)/Single(B_Const));
>
> Var_Ans1:= Extended(Byte(A_Var)/Single(B_Var));
>
> WRITELN ( ' Const_Ans1 = ', Const_Ans1);
>
> WRITELN ( 'Var_Ans1 = ',Var_Ans1);
>
> End.
>
> Const_Ans1 =2.85714298486709594727E-0001
>
> Var_Ans1 =2.85714285714285714282E-0001
>
> Windows 10 Calculator shows the answer to be
>
> 0.28571428571428571428571428571429Which matches up with the way 
> variables have done this math, not the way constants have done it.
>

you don't need a calculator for 2 / 7 or 1 / 3.5. There is a simple rule 
for the decimal representation when dividing by 7:

1 / 7 = 0.142857 ...   repeat ad infinitum
2 / 7 = 0.285714
3 / 7 = 0.428571
4 / 7 = 0.571428
5 / 7 = 0.714285
6 / 7 = 0.857142

you see the pattern? You simply have to rotate the six digits in a 
certain manner ...

> I am explicitly casting everything I possibly can.
>

I don't think you need the cast to extended around the divisions;
the divisions are done at different precision, which makes your problem,
but the cast to extended at the end doesn't help ... it will be done 
anyway,
because the target field is extended.

The problem indeed is that the division is done differently for consts 
and for vars,
and this seems to be the case for Windows only, as another poster 
pointed out.
This seems to be a real bug.

When casting this way

Byte(A_Var)/Single(B_Var)

I would expect the division to be done with single precision, but 
apparently it is done
using extended (or another) precision ... on Windows, not on Linux. And 
this is what
causes your headaches.


> Without the :20:20 you can see that the result of each of these is in 
> fact extended, but they are VERY different numbers, even though my 
> casting is IDENTICAL , and I can’t make it any more the same, the 
> results are very different.Math with Variables allows the result of a 
> low precision entity, in this case a Byte, divided by a low precision 
> entity, in this case a Single, to be calculated and stored in an 
> Extended, Math with Constants does not allow this possibility, and 
> this is where all the confusion is coming from.Two identical pieces of 
> code not producing the same results.
>
> Math with Constants is NOT the same as Math with Variables, and if 
> this one thing was fixed, then all the other problems go away.
>
> I am doing:
>
> Const_Ans1 := Extended(Byte(A_Const)/Single(B_Const));
>
> Var_Ans1:= Extended(Byte(A_Var)/Single(B_Var));
>
> Just to make a point, but the code:
>
> Const_Ans1 := A_Const/B_Const;
>
> Var_Ans1:= A_Var/B_Var;
>
> Should also produce identical results without re-casting, because 
> A_Const and A_Var are both defined to be a Byte, and B_Const and B_Var 
> are both defined to be a Single, and Const_Ans1 and Var_Ans1 are both 
> defined to be Extended.
>
> Why are the result different?
>
> As I tried to explain before, if I force all constants to be Extended:
>
> Const_Ans1 := Extended(Extended(A_Const)/Extended(B_Const));
>
> Then I do get the correct results, but this should not be needed, and 
> this casting is wrong,because a byte divided by a single should be 
> able to be extended without first storing them in extended entities, 
> the same as it is with variables.
>
> With variables I do not need to re-cast every single term in an 
> expression as Extended to get an Extended answer.
>
> With constants this is the ONLY way I can get an extended answer.
>
> Before the changes to 2.2, all constants WERE at highest precision, so 
> the math involving constants never had to bother with considering that 
> a low precision number divided by a low precision number could end up 
> as an extended, because there were no low precision constants at all. 
> But now there are, and that’s really fine, because we often have low 
> precision variables, and that’s fine, but the math needs to be done 
> the same way whether with constants or variables to produce identical 
> results so now math with constants also has to take into consideration 
> that math with low precision entities can and often does result in a 
> high precision answer.
>
> To demonstrate that a low precision entity divided by a low precision 
> entity should always be able to be an Extended, use this example my 
> constants as BYTES so there can be no lower precision:
>
> program Const_Vs_Var;
>
> Const
>
> A_const = Byte(2);
>
> B_const = Byte(7);
>
> Var
>
> A_Var : Byte;
>
> B_Var : Byte;
>
> Const_Ans1, Const_Ans2, Var_Ans1 : Extended;
>
> Begin
>
> A_Var := Byte(A_Const);
>
> B_Var := Byte(B_Const);
>
> Const_Ans1 := A_Const/B_Const;
>
> Var_Ans1:= A_Var/B_Var;
>
> WRITELN ( ' Const_Ans1 = ', Const_Ans1);
>
> WRITELN ( 'Var_Ans1 = ',Var_Ans1);
>
> End.
>
> Const_Ans1 =2.85714285714285714282E-0001
>
> Var_Ans1 =2.85714285714285714282E-0001
>
> Now as you can see math with constants is EXCATLY the same as math 
> with variables again, and I did not need to do any ridiculous casting 
> to get the correct answer.
>
> I hope this makes sense.I know exactly what is happening, but I don’t 
> know why and I don’t know how to explain it other than to give these 
> examples.
>

This is different, because now you are promoting integers to FP values 
(because the Pascal standard says,
that the disivion by slash always is a FP division), and this time you 
get extended precision, apparently.


> 1/3.5 == 2/7
>
> What should this program produce?
>
> program Const_Vs_Var;
>
> Const
>
> A_const = (2/7)-(1/3.5);
>
> Begin
>
> WRITELN ( ' A_Const = ', A_Const);
>
> End.
>

Tricky ...
1 / 3.5 ... because the current implementation of FPC assigns type 
single to the constant 3.5, this division is done using single precision
2 / 7 ... promote the integers to extended ... this division is done 
using extended precision

See my proposal from the earlier posts. If my suggestion would be 
followed, the computation 1 / 3.5 would by done
using maximum precision, and there wouldn't be a problem.


> How can this possibly be right?
>
> A_Const= -1.27724238804447203649E-0008
>
> James
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freepascal.org/pipermail/fpc-pascal/attachments/20240220/c73179ac/attachment-0001.htm>


More information about the fpc-pascal mailing list