Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2024-02

Floating point question

29 messages · 12 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Ern Aldo

Compile-time math needs to be as correct as possible. RUN-time math can worry about performance. The whole point of computer programming is to let the machine do the hard work up front.

Regarding optimizations for speed at the expense of mathematical correctness, they need to default to Off, and be switchable to ON for compile-time math. Runtime math *maybe* could default to On, but since this is a change in correctness for program output, it needs to be announced as a much bigger deal as far as source-compatibility with existing code.

On 2024-02-13 06:00, wrote:
the compiler math is ... reducing each term of an expression ... and the answer is not coming out right.

this would result in less efficient code, since all the math will then be done at full precision, which is slower. it is a trade-off between size (=precision) and speed.

[in] compile time math, performance doesn't count

Reply by email

Florian Klämpfl
to Ern Aldo
Am 16.02.2024 um 08:23 schrieb Ern Aldo via fpc-pascal :  Compile-time math needs to be as correct as possible. RUN-time math can worry about performance.

So you are saying when constant propagation is on, an expression should have a different result than with constant propagation off?

Reply by email

Bernd Oppolzer
to Florian Klämpfl
Florian Klämpfl wrote
Am 16.02.2024 um 08:23 schrieb Ern Aldo via fpc-pascal : >  Compile-time math needs to be as correct as possible. RUN-time math can worry about performance. So you are saying when constant propagation is on, an expression should have a different result than with constant propagation off?

I don't know exactly, what you mean by constant propagation.

But IMO, given this (sort of fictive) Pascal code snippet:

const Xconst : single = 1440.0;
var y1, y2 : real;
y1 := 33.0 / 1440.0;
y2 :=  33.0 / Xconst;

the division in the first assignment (to y1) should be done at maximum precision, that is,
both constants should be converted by the compiler to the maximum available precision and
the division should be done (best at compile time) using this precision.

in the second case, if the compiler supports constants of the reduced type (which I believe it does,
no matter how the syntax is), I find it acceptable if the computation is done using single precision,
because that's what the developer calls for.

So probably the answer to your question is: yes.

Kind regards

Bernd

Reply by email

James Richters
to Florian Klämpfl: So you are saying when constant propagation is on, an expression should have a different…
So you are saying when constant propagation is on, an expression should have a different result than with constant propagation off?

The result of math when using constants MUST be the same as the result of identical math using variables.

There should never be a difference if I did my formula with hard coded constants vs variables.

Const_Ans = 2.00000010627116630224
Const_Ans1 = 2.00000010627116630224
Var_Ans1 = 2.00000000000000000000

This should not be happening.

James

Reply by email

Bernd Oppolzer
to James Richters
James Richters wrote 12 lines
> So you are saying when constant propagation is on, an expression should have a different result than with constant propagation off? The result of math when using constants MUST be the same as the result of identical math using variables. There should never be a difference if I did my formula with hard coded constants vs variables. Const_Ans = 2.00000010627116630224 Const_Ans1 = 2.00000010627116630224 Var_Ans1 = 2.00000000000000000000 This should not be happening. James
See my other post;

if the developer explicitly wants reduced precision, then this is what happens.
But the reduced precision should not come unexpectedly simply because the compiler attaches type attributes to constants (which can't be easily explained),
and then the outcome of simple decimal arithmetic is incorrect.

So I have to disagree, sorry.

Reply by email

James Richters
to Bernd Oppolzer
But the reduced precision should not come unexpectedly simply because the

compiler attaches type attributes to constants (which can't be easily

explained), and then the outcome of simple decimal arithmetic is incorrect.

How are you disagreeing? This is EXACTLY what I am saying. My math with variables is ALWAYS correct, my math with Constants is not coming out the same and can't be easily explained. I guess I should have re-posted the example because it's not as it seems, this is a counterexample I had provided to show the incorrect math indeed could force an INCREASE in Precision, but even if I don't cast the variables you still get the same unexpected results.. this is just to demonstrate that the problem is not the reduction and assignment itself, but it's the way calculations are done by the compiler being wrong.

The math with constants is what's wrong, not the reduction in precision. Look closely at the following example. I am ALWAYS adding an integer to a byte divided by a Single... The reduction in precision has been removed from this example because I explicitly cast what I wanted.

This proves that the math with constants is what the problem is, not that the constants themselves were reduced in precision.

The results of Const_Ans1 MUST Equal Var_Ans1 EXACTLY or all kinds of problems will arise.

I have no reason to expect that the results of Var_Ans1 and Const_Ans1 would be different, yet they are. THIS is the Problem, and that's pretty much exactly what you just said, so I believe we are in agreement.

The problem isn't that the reduction in precision assigned a type attribute to the constant, it's that the math with that type is being done wrong, because I can do the math with the EXACT same typed variables and it comes out correctly. I'm trying to show where the real problem is, (not very successfully)

Math with Variables in the executing program, a byte / single can very well be extended.
With the compiler math a byte / single is FORCED to be a single, which is incorrect.
Dividing by a single does NOT imply the result should be a single.

Before the changes in v2.2, this was NEVER an issue, because all the constants were full precision, so this could never possible come up as there would be no math with a single in it.

My point is, no matter what is wrong, the result of math with constants must always be exactly the same as math with variables otherwise no one can figure out what the heck is going on.

James

program Const_Vs_Var;
Const
   A_const = Integer(8427);
   B_const = Byte(33);
   C_const = Single(1440.5);
   Win_Calc = 16854.045817424505380076362374176;
   Const_Ans = 16854.045817424505380076362374176 / (8427 + 33 / 1440.5); Var
   A_Var : Integer;
   B_Var : Byte;
   C_Var : Single;
   Const_Ans1, Var_Ans1 : Extended;
Begin
   A_Var := A_Const;
   B_Var := B_Const;
   C_Var := C_Const;
   Var_Ans1   := Win_Calc / (A_Var+B_Var/C_Var);
   Const_Ans1 := Win_Calc / (A_Const+B_Const/C_Const);
   WRITELN ( '  Const_Ans = ',  Const_Ans:20:20);
   WRITELN ( ' Const_Ans1 = ', Const_Ans1:20:20);
   WRITELN ( '   Var_Ans1 = ',   Var_Ans1:20:20);
End.

The result is:
Const_Ans = 2.00000010627116630224
Const_Ans1 = 2.00000010627116630224
Var_Ans1 = 2.00000000000000000000

When I do:
   Var_Ans1   := Win_Calc / (A_Var+B_Var/C_Var);
   Const_Ans1 := Win_Calc / (A_Const+B_Const/C_Const);
Earlier message 36 lines
-----Original Message----- From: fpc-pascal On Behalf Of Bernd Oppolzer via fpc-pascal Sent: Friday, February 16, 2024 10:48 AM To: James Richters via fpc-pascal Cc: Bernd Oppolzer Subject: Re: [fpc-pascal] Floating point question Am 16.02.2024 um 15:57 schrieb James Richters via fpc-pascal: >> So you are saying when constant propagation is on, an expression should have a different result than with constant propagation off? > The result of math when using constants MUST be the same as the result of identical math using variables. > > There should never be a difference if I did my formula with hard coded constants vs variables. > > Const_Ans = 2.00000010627116630224 > Const_Ans1 = 2.00000010627116630224 > Var_Ans1 = 2.00000000000000000000 > > This should not be happening. > > James See my other post; if the developer explicitly wants reduced precision, then this is what happens. But the reduced precision should not come unexpectedly simply because the compiler attaches type attributes to constants (which can't be easily explained), and then the outcome of simple decimal arithmetic is incorrect. So I have to disagree, sorry.

Reply by email

Unknown author
to James Richters: The result of math when using constants MUST be the same as the result of identical math…
James Richters wrote 11 lines
> So you are saying when constant propagation is on, an expression should have a different result than with constant propagation off? The result of math when using constants MUST be the same as the result of identical math using variables. There should never be a difference if I did my formula with hard coded constants vs variables. Const_Ans = 2.00000010627116630224 Const_Ans1 = 2.00000010627116630224 Var_Ans1 = 2.00000000000000000000 This should not be happening.

i've been quietly reading this entire thread and wow... i mean i do fully understand the situation and both sides of the problem... looking at the numbers, alone, the difference hits me hard in my OCD and i do agree with the assessment that max precision should be used for the const math and the result reduced when possible... but then, on the other hand, it really really really depends, _a lot_, on exactly /what/ is being calculated and what the numbers represent...

for example, here on Earth, (7 decimal places) 0.0000001 degree latitude is ""only"" 1cm... (8 decimal places) 0.00000001 degree latitude is ""only"" 1mm... longitude, on the other hand, is variable such that 7 decimal places at the equator is the same as latitude but as you move toward the poles, it changes such that 4 decimal places is 20cm...

for me, this can be important when it comes to placing scenery objects in a "true to life" simulator of the Earth, its terrain, and building/object placement... a building or road being 1cm out of position isn't noticeable... even 20cm out of place isn't really noticeable...

if this math is being used for measuring parsecs, yeah, i don't think i want to be on that space ship when it arrives at the wrong position in space... if this math is being used to measure angstroms, yeah... it kinda of matters... if this math is being used for cutting wood as a carpenter does, it doesn't matter so much... so please, put some perspective on this problem...

i'll have to look at my code for calculating a satellite's position in space in relation to Earth's center... especially depending on the satellite's altitude above the Earth as the higher the satellite is, the further off position the calculation could be... depending on what the calculation is being used for, it could be the difference between a good pass of two satellites crossing paths or it could be quite the ugly loss of two satellites plus a huge spew of debris from their impact...

NOTE: No off-list assistance is given without prior approval. *Please keep mailing list traffic on the list where it belongs!*

Reply by email

Michael Van Canneyt
to Unknown author
wkitty42--- wrote 6 lines
On 2/16/24 9:57 AM, James Richters via fpc-pascal wrote: >> So you are saying when constant propagation is on, an expression should >> have a different result than with constant propagation off? > > The result of math when using constants MUST be the same as the result of > identical math using variables.

As far as I can see, in that case you must simply type your constants.

Variables are always typed - their precision is determined by the type you have specified.

For constants, the compiler will choose a type and consequently the precision. Jonas and others have explained the rules that the compiler uses.

If you don't like the rules that the compiler uses, you can set a type for your constants. When you explicitly set a type, you are also specifying the precision of the calculation.

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

There are 2 ways to do so:

Const
   MyConst : Double = 1.23e45;

or

Const
   MyConst = Double(1.23e45);

The latter can also be used in an expression

  X := Y * Double(1.23e45);

There can be discussion about the rules that the compiler uses when it chooses a type, but any given set of rules will always have consequences that may or may not be desirable.

Possibly some compiler switches can be invented that modify the compiler's rules for the constant type to use.

(incidentally, this is one of the reasons the FPC team does not want to make inline variables as Delphi does, since there the type will again be determined by the compiler - just as for constants, leading to ambiguity...)

Michael.

Reply by email

Bernd Oppolzer
to Michael Van Canneyt
Michael Van Canneyt wrote 9 lines
There can be discussion about the rules that the compiler uses when it chooses a type, but any given set of rules will always have consequences that may or may not be desirable. Possibly some compiler switches can be invented that modify the compiler's rules for the constant type to use.

If the rules at the moment make this a single:

const xs = 64.015625;       { 64 + 1 / 64 }

because it can be represented correctly (without rounding error) in a binary single FP IEEE representation,
and this a double or extended type:

const xd = 64.1;      { no finite representation in binary or hex }

with all the observed effects on computations that the other posters here have pointed out

my personal opinion would be:

- never use such (implicitly typed) definitions ... but that's standard Pascal, after all
- try to convince the compiler builders that we need a better solution here

IMO, a compiler switch that gives all FP constants the best available precision would solve the problem -
BTW: WITHOUT forcing expressions where they appear to use this precision, if the other parts of the expression
have lower precision.

In fact, when parsing and compiling the expressions, you always can break the problem down to TWO operands
that you have to consider, and if one of them is a literal constant, it should not force the type of the operation to
a higher precision ... that's what I would do.

That's why I write all those mails (although I am not an active FPC user), because I want all Pascal versions around
to implement a clear and UNDERSTANDABLE language without strange effects.

Kind regards

Bernd

Show quoted text 10 lines
(incidentally, this is one of the reasons the FPC team does not want to make inline variables as Delphi does, since there the type will again be determined by the compiler - just as for constants, leading to ambiguity...) Michael. _______________________________________________ fpc-pascal maillist  -  https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

Reply by email

Bernd Oppolzer
to Bernd Oppolzer
Bernd Oppolzer wrote 14 lines
IMO, a compiler switch that gives all FP constants the best available precision would solve the problem - BTW: WITHOUT forcing expressions where they appear to use this precision, if the other parts of the expression have lower precision. In fact, when parsing and compiling the expressions, you always can break the problem down to TWO operands that you have to consider, and if one of them is a literal constant, it should not force the type of the operation to a higher precision ... that's what I would do.

Commenting on my own post (this time):

const xs : single = 1440.5;
       xd : double = 1440.5;
       xu = 1440.5;              /* double or single, depending on new
option */
       z : single = 33.0;
y1 := xs / z;   { single precision }
y2 := xd / z;   { double precision }
y3 := xu / z;   { different result, depending on new option }
y4 := 1440.5 / z;   { single, because z dictates it, independent of
option }
y5 := 1440.1 / z;   { IMO: single, because z dictates it, independent of
option }
y6 := 1440.5 / 33.0;   { depending on new option }

This may be in contrast to what's today done in FPC, but that's how I (personally) would like to have it done. Maybe the behaviour without the new option set is the same as now.

Not sure about y5.

Kind regards

Bernd

Reply by email

Florian Klämpfl
to Bernd Oppolzer: I don't know exactly, what you mean by constant propagation. But IMO, given this (sort of…
Show quoted text 24 lines
Am 16.02.2024 um 15:34 schrieb Bernd Oppolzer via fpc-pascal : Am 16.02.2024 um 08:32 schrieb Florian Klämpfl via fpc-pascal: > Am 16.02.2024 um 08:23 schrieb Ern Aldo via fpc-pascal : >> >>  Compile-time math needs to be as correct as possible. RUN-time math can worry about performance. > So you are saying when constant propagation is on, an expression should have a different result than with constant propagation off? I don't know exactly, what you mean by constant propagation. But IMO, given this (sort of fictive) Pascal code snippet: const Xconst : single = 1440.0; var y1, y2 : real; y1 := 33.0 / 1440.0; y2 := 33.0 / Xconst; the division in the first assignment (to y1) should be done at maximum precision, that is, both constants should be converted by the compiler to the maximum available precision and the division should be done (best at compile time) using this precision.

Constant folding is an optimization technique, so the first expression could be also evaluated at run time in case of a simple compiler (constant folding is not something which is mandatory) which means that we have to use always full precision (what full means depends on the host and target platform thought) for real operations. So either: always full precision with the result all operations get bloated or some approach to assign a precision to real constants.

It gets even more hairy if more advanced optimization techniques are involved:

Consider

var
   y1,y2 : single;
 y1 := 1440.0
 y2 := 33.0 / y1;

When constant propagation and constant folding are on (both are optimizations), y2 can be calculated at compile time and everything reduced to one assignment to y2. So with your proposal the value of y2 would differ depending on the optimization level.

Show quoted text 22 lines
in the second case, if the compiler supports constants of the reduced type (which I believe it does, no matter how the syntax is), I find it acceptable if the computation is done using single precision, because that's what the developer calls for. So probably the answer to your question is: yes. Kind regards Bernd _______________________________________________ fpc-pascal maillist - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

Reply by email

Bernd Oppolzer
to Florian Klämpfl
Florian Klämpfl wrote 23 lines
> const Xconst : single = 1440.0; > > var y1, y2 : real; > > y1 := 33.0 / 1440.0; > > y2 :=  33.0 / Xconst; > > the division in the first assignment (to y1) should be done at > maximum precision, that is, > both constants should be converted by the compiler to the maximum > available precision and > the division should be done (best at compile time) using this precision. > Constant folding is an optimization technique, so the first expression could be also evaluated at run time in case of a simple compiler (constant folding is not something which is mandatory) which means that we have to use always full precision (what full means depends on the host and target platform thought) for real operations. So either: always full precision with the result all operations get bloated or some approach to assign a precision to real constants.

no problem here; the result of y1 must be the same, no matter if the computation is done at compile time or at run time. the result should always be computed at the best precision available, IMO (maybe controlled by a compiler option,
which I personally would set).

y2: the computation could be done using single precision, because the second operand says so.
IMO: even if the first operand was a literal constant which cannont be represented exactly in a single FP field

Show quoted text 15 lines
It gets even more hairy if more advanced optimization techniques are involved: Consider var    y1,y2 : single;  y1 := 1440.0  y2 := 33.0 / y1; When constant propagation and constant folding are on (both are optimizations), y2 can be calculated at compile time and everything reduced to one assignment to y2. So with your proposal the value of y2 would differ depending on the optimization level.

if y2 is computed at compile time (which is fine), then the result IMO is determined by the way the source code is written. A possible optimization must not change the meaning of the program, given by the source code.
So in this case, the compiler would have to do a single precision division (if we could agree on the rules that we discussed so far), and the meaning of the program may not be changed by optimization techniques (that is: optimization may not change the result to a double or extended precision division ... otherwise the optimization is wrong).

BTW: many of the ideas about what a compiler should do come from my 30+ years experience with PL/1.
That may be a sort of "deformation professionelle", as the French call it, but that's how it is.

Apart from the proper handling of literal FP constants (which is what we discuss here, IMO), there is another topic,
which is IMO also part of the debate:

does

  y2 := 33.1 / y1;

require the division to be done at single precision or not?

We have here a literal constant, which is NOT single (33.1) and a single variable operand.
I understood from some postings here, that some people want the divisions with singles carried out using
single arithmetic, for performance reasons, so I asked for a single division here (in my previous postings).
But IMO that's different in the current implementation ... what do others think about this?

I, for my part, would find it strange, if the precision of the division in this case would depend on the (implicit)
type of the operand, that is:

  y2 := 33.015625 / y1;  { single precision, because constant is single
- 33 + 1 / 64 }
  y2 := 33.1 / y1;   { extended precision, because constant is extended }

IMO, both of these divisions should be done at single precision, controlled by the type of y1.
But this could be controlled by ANOTHER new option, if someone asks for it.

Kind regards

Bernd

Reply by email

James Richters
to Bernd Oppolzer
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.28571428571428571428571428571429 Which matches up with the way variables have done this math, not the way constants have done it.

I am explicitly casting everything I possibly can.

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 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.

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.

How can this possibly be right?
A_Const = -1.27724238804447203649E-0008

James

Reply by email

Michael Van Canneyt
to James Richters
James Richters wrote 31 lines
> 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.28571428571428571428571428571429 Which matches up with the way variables have done this math, not the way constants have done it.

I would not put too much trust in Windows calculator, since there you have no control over the seems to be a windows-specific problem. Here is the result of your program when executed on Linux:

Const_Ans1 = 2.85714298486709594727E-0001
Var_Ans1 = 2.85714298486709594727E-0001

As you can see, the result is identifical.

As for the explanation, I will have to leave that to the compiler developers.

Michael.

Reply by email

Bernd Oppolzer
to James Richters: This is what I have been trying to point out. Math with identical casting with variables…

See below ...

James Richters wrote 51 lines
>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.

Show quoted text 105 lines
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.

Show quoted text 16 lines
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.

Show quoted text 6 lines
How can this possibly be right? A_Const= -1.27724238804447203649E-0008 James

Reply by email

James Richters
to Michael Van Canneyt: I would not put too much trust in Windows calculator, since there you have no control…
I would not put too much trust in Windows calculator, since there you have

no control over the actual CORRECT answer according to https://www.wolframalpha.com/input?i=1%2F3.5 Is
0.285714 Repeating forever

Which is what I get on windows only when using Variables. Var_Ans1 = 2.85714285714285714282E-0001
Which as you can see the .285714 repeats exactly 3 times with two more correct digits, for a total of 20 correct digits before we run out of precision.

It seems to be a windows-specific problem. Here is the result of your

program when executed on Linux:

Const_Ans1 = 2.85714298486709594727E-0001 Var_Ans1 = 2.85714298486709594727E-0001 As you can see, the result is identical.

Great, Linux is consistently giving you the wrong answer, and I notice it's the same wrong I answer I get when I use constants.

I don't know why it would be different in Windows than on Linux. I am curious what you get in Linux for:
A_const = (2/7); // should be 0.285714 Repeating forever also Or
B_const = (2/7)-(1/3.5); //should always be 0;

Just for fun I ran it on Turbo Pascal 7.0 for DOS and got: Const_Ans1 = 2.85714285714286E-0001
Var_Ans1 = 2.85714285714286E-0001

I also noticed that re-casting constants is not allowed by Turbo Pascal 7.0, so I removed the unnecessary re-casting so it would compile, but I did get the expected answer even without re-casting it... as I expected I should. The fact that Turbo Pascal gives be the correct answer to 14 digits even though in Turbo Pascal I also divided a byte by a single shows that I should expect my pascal programs to provide THIS correct answer, 0.285714 repeating until you run out of precision, not something else. And it shows that a byte divided by a single is in fact able to be an extended... it also proves that in Pascal the result of a byte divided by a single is NOT limited to single precision.

2.85714298486709594727E-0001 makes no sense to me at all, It's like it did the calculation in Single precision, then stored it in an extended, but if I wanted to work in single precision, I would not have set my variable to extended.

There is some flawed logic somewhere that says if you do math with a single, the result is a single, but this is simply not correct, you can divide a byte by a byte and get an extended, and you can divide a single by a single and get an extended, Pascal has ALWAYS worked that way. Forcing the result of an equation to be a single because one term is a single is just not right.

James

Reply by email

Sven Barth
to James Richters

James Richters via fpc-pascal schrieb am
Di., 20. Feb. 2024, 04:42:

I don't know why it would be different in Windows than on Linux.

If you're using Win64, then the answer is simple: x86_64-win64 unlike any other x86 target does not support Extended, so neither the compiler nor the code in runtime will ever calculate anything with that precision.

Regards,
Sven

Reply by email

Tomas Hajny
to Sven Barth
Sven Barth wrote 9 lines
James Richters via fpc-pascal schrieb am Di., 20. Feb. 2024, 04:42: > I don't know why it would be different in Windows than on Linux. If you're using Win64, then the answer is simple: x86_64-win64 unlike any other x86 target does not support Extended, so neither the compiler nor the code in runtime will ever calculate anything with that precision.

Well, this probably isn't the (sole) reason in this particular case, because the results posted by Michael and coming from Linux (probably x86_64) were equal for constants and variables, but less precise than the result of run-time posted for Windows (unfortunately without specifying whether it was i386-win32 or x86_64-win64, but I'd guess for the former based on the results - exactly due to the reason mentioned by you).

Tomas

Reply by email

James Richters
to Tomas Hajny
>If you're using Win64, then the answer is simple: x86_64-win64 unlike any

other x86 target does not support Extended, so neither the compiler nor the code in runtime will ever calculate anything with that precision. To clarify, I am using i386-win32 on a 64 bit specifically because Extended is really just a Double on x86_64-win64. All my test programs were done with Win32.

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

manner ...
I see it now that you pointed it out and I think that it is really cool that it's the same digits rotated! Thanks!

>I don't think you need the cast to extended around the divisions;

Correct, I don't need to do this and I should not need to do it, but I also should not need to re-cast the terms of the division, but with constants that's what I must do to get the correct result.

Also, my programs would never re-cast the constants, I was just making it clear that a byte divided by a single in fact does produce a correct extended answer when done with variables, and I didn't want any doubt that it was dividing a byte by a single.
This is the correct behavior. My point was that the problem isn't that the 3.5 was stored or cast as a single, It's valid for it to be a single, and that should make no difference at all, and in fact my results as exactly the same without all the casting,
But the only way to get the correct answer with constants is to do what should be an unnecessary cast to extended of terms in my expression:

program Const_Vs_Var;
Const
   A_const = 1;
   B_const = 3.5;
Var
   A_Var : Byte;
   B_Var : Single;
   Const_Ans1, Var_Ans1, Difference1 : Extended;
   Const_Ans2, Var_Ans2, Difference2 : Extended;
Begin
   A_Var := A_Const;
   B_Var := B_Const;
   Const_Ans1  := A_Const/B_Const;
   Var_Ans1    := A_Var/B_Var;
   Difference1 := Var_Ans1-Const_Ans1;
   Const_Ans2  := A_Const/Extended(B_Const);   //I should not need to cast
B_Const in this way
   Difference2 := Var_Ans1-Const_Ans2;
   WRITELN ( '  Const_Ans1 = ', Const_Ans1);
   WRITELN ( '    Var_Ans1 = ', Var_Ans1);
   WRITELN ( ' Difference1 = ', Difference1);
   Writeln;
   WRITELN ( '  Const_Ans2 = ', Const_Ans2);
   WRITELN ( '    Var_Ans1 = ', Var_Ans1);
   WRITELN ( ' Difference2 = ', Difference2);

End.
Const_Ans1 = 2.85714298486709594727E-0001 //This is a single precision calculation stored in an extended
Var_Ans1 = 2.85714285714285714282E-0001 //The nice repeating decimal I expect
Difference1 = -1.27724238804447203649E-0008 //This should have been 0

Const_Ans2 = 2.85714285714285714282E-0001 //I should not have had to cast Extended(B_Var) to get this
Var_Ans1 = 2.85714285714285714282E-0001 // The correct answer again just for clarification
Difference2 = 0.00000000000000000000E+0000 //Now it is 0 as I expected

>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.

I would NOT expect this to result in single precision, when I divide a Byte by a single in Turbo Pascal and assign it to a Double, the result is correct in double precision.
The ONLY way to get a single for an answer in Turbo Pascal is to define a variable as a single and use that to do that calculation. MySingle := A_Var/B_Var;

If the variable is a double:
MyDouble := A_Var/B_Var;
Then no matter what B_Var is, whether it's a single or a double, MyDouble is the same correct number

If I want the result to be a single then:
Single(A_Var/B_Var)
Should be what I require.

It doesn't matter in Turbo Pascal if I am dividing by a variable defined as a single or a variable defined as a double, or an undefined constant, and it does not matter in FPC either as long as my division is done with variables.

It's FPC with Constants that is making MyByte/MySingle come out as a single, and that is incorrect. Division by a single does not force the answer to be a single.
Please See this:

program Const_Vs_Var;
Const
   A_const = 1;
   B_Const = 3.5;
   C_Const = 7;
Var
   A_Var : Byte;
   B_Var : Single;
   C_Var : Byte;
   VPi : Extended;
   Const_Ans1, Var_Ans1, Difference1 : Extended;
   Const_Ans2, Var_Ans2, Difference2 : Extended;
   Const_Ans3, Var_Ans3, Difference3 : Extended;
Begin
   A_Var := A_Const;
   B_Var := B_Const;
   C_Var := C_Const;
   VPi := Pi;
   Const_Ans1  := A_Const/B_Const;
   Var_Ans1    := A_Var/B_Var;
   Difference1 := Var_Ans1-Const_Ans1;
   Const_Ans2  := A_Const/C_Const;
   Var_Ans2    := A_Var/C_Var;
   Difference2 := Var_Ans2-Const_Ans2;
   Const_Ans3  := Pi/B_Const;
   Var_Ans3    := VPi/B_Var;
   Difference3 := Var_Ans3-Const_Ans3;
   WRITELN ( '  Const_Ans1 = ', Const_Ans1);
   WRITELN ( '    Var_Ans1 = ', Var_Ans1);
   WRITELN ( ' Difference1 = ', Difference1);
   Writeln;
   WRITELN ( '  Const_Ans2 = ', Const_Ans2);
   WRITELN ( '    Var_Ans2 = ', Var_Ans2);
   WRITELN ( ' Difference2 = ', Difference2);
   Writeln;
   WRITELN ( '  Const_Ans3 = ', Const_Ans3);
   WRITELN ( '    Var_Ans3 = ', Var_Ans3);
   WRITELN ( ' Difference3 = ', Difference3);
End.

Const_Ans1 = 2.85714298486709594727E-0001
Var_Ans1 = 2.85714285714285714282E-0001
Difference1 = -1.27724238804447203649E-0008

Const_Ans2 = 1.42857142857142857141E-0001
Var_Ans2 = 1.42857142857142857141E-0001
Difference2 = 0.00000000000000000000E+0000

Const_Ans3 = 8.97597901025655211019E-0001
Var_Ans3 = 8.97597901025655211019E-0001
Difference3 = 0.00000000000000000000E+0000

If division by a single forced the answer to be a single, then Const_Ans3 := Pi/B_Const; produce a single
But it does not, it correctly produces extended results with both Variables and Constants.

Const_Ans1 := A_Const/B_Const; is incorrectly being evaluated as a single and I don't know why.
The fact that there is something in Difference1 but not Difference2 or Difference3 is why I think there is a bug somewhere. I am expecting my byte divided by a single to be an extended for both variables and constants, but only variables is working.

Surely if a Byte divided by a byte can result in an extended than a Byte divided by a single should also result in an extended.

It's like this for everything, you can add a byte to a byte and get a word for an answer,
the result of any math can always result in higher precision than all of the terms involved,
otherwise there would be no point to double precision or extended precision. I have never needed to cast my terms to get expected results. Are we really supposed to do:

MyConstant = Extended(2.5)/Extended(3.5); to get it to be an extended?

MyConstant = 2.5/3.5 should result in an extended, I shouldn't need the casting.

James

Reply by email

Thomas Kurz
to Sven Barth: James Richters via fpc-pascal schrieb am Di., 20. Feb. 2024, 04:42: If you're using…
If you're using Win64, then the answer is simple: x86_64-win64 unlike any other x86 target does not support Extended, so neither the compiler nor the code in runtime will ever calculate anything with that precision.

That's another thing I've never understood. How can it depend on the OS? It's the CPU which does math, and I don't understand what the OS has to do with that? If amd64 architecture didn't support the extended-type at all, I'd say "ok". But it's supported on Linux but not on Windows? Huh?

Reply by email

Tomas Hajny
to Thomas Kurz: That's another thing I've never understood. How can it depend on the OS? It's the CPU…
Thomas Kurz wrote 11 lines
> If you're using Win64, then the answer is simple: x86_64-win64 unlike > any > other x86 target does not support Extended, so neither the compiler > nor the > code in runtime will ever calculate anything with that precision. That's another thing I've never understood. How can it depend on the OS? It's the CPU which does math, and I don't understand what the OS has to do with that? If amd64 architecture didn't support the extended-type at all, I'd say "ok". But it's supported on Linux but not on Windows? Huh?

The reason is that the operating system is among others responsible for controlling the multitasking. That includes saving the context of the originally active task and restoring the context of the new task to the original state before it was interrupted. If Win64 doesn't guarantee restoring FPU registers specific to the extended type, using this type would get you into troubles despite its support in the FPU.

Tomas

Reply by email

Thomas Kurz
to Unknown author: i've been quietly reading this entire thread and wow... i mean i do fully understand the…
Show quoted text 5 lines
for example, here on Earth, (7 decimal places) 0.0000001 degree latitude is ""only"" 1cm... (8 decimal places) 0.00000001 degree latitude is ""only"" 1mm... longitude, on the other hand, is variable such that 7 decimal places at the equator is the same as latitude but as you move toward the poles, it changes such that 4 decimal places is 20cm...

My initial problem (i.e. why I asked the original question -- which I do regret meanwhile *g*) was that we do use floating-point numbers for date and time operations. (TDateTime = Double)

And I had discrepancies of about 40 seconds when converting between astronomical dates and TDateTime. This was how it all started...

We need approx. 5 decimals to represent one second (because the non-fractional part is considered being the day number). So "single" precision isn't acceptable here. If TDateTime were Unix-timestamp, it wouldn't matter. But since TDateTime is Julian day number (maybe with an offset, but that's irrelevant here), it unfortunately does matter.

Reply by email

Thomas Kurz
to Tomas Hajny: The reason is that the operating system is among others responsible for controlling the…

Aaaah, ok. Thank you very much for alrifying this long-standing question!

Earlier message 27 lines
----- Original Message ----- From: Tomas Hajny via fpc-pascal To: FPC-Pascal users discussions Sent: Thursday, February 22, 2024, 15:25:34 Subject: [fpc-pascal] Floating point question On 2024-02-22 15:08, Thomas Kurz via fpc-pascal wrote: >> If you're using Win64, then the answer is simple: x86_64-win64 unlike >> any >> other x86 target does not support Extended, so neither the compiler >> nor the >> code in runtime will ever calculate anything with that precision. > That's another thing I've never understood. How can it depend on the > OS? It's the CPU which does math, and I don't understand what the OS > has to do with that? If amd64 architecture didn't support the > extended-type at all, I'd say "ok". But it's supported on Linux but > not on Windows? Huh? The reason is that the operating system is among others responsible for controlling the multitasking. That includes saving the context of the originally active task and restoring the context of the new task to the original state before it was interrupted. If Win64 doesn't guarantee restoring FPU registers specific to the extended type, using this type would get you into troubles despite its support in the FPU. Tomas

Reply by email

Thomas Kurz
to Michael Van Canneyt: As far as I can see, in that case you must simply type your constants. Variables are…
Show quoted text 5 lines
For constants, the compiler will choose a type and consequently the precision. Jonas and others have explained the rules that the compiler uses. If you don't like the rules that the compiler uses, you can set a type for your constants. When you explicitly set a type, you are also specifying the precision of the > calculation.

If the ruleset won't change - and from what I've read from the developers, it won't change - could we please have the compiler issue a warning (or a hint) if a loss in precision happens.

E.g. "Warning: constant reduced to single precision. Use {$MINFPCONSTPREC} or Double() to keep full precision."

I am aware about this behavior now, but nevertheless I'd like to get warned if I forget about either of those.

Reply by email

James Richters
to Jean SUZINEAU

I seem to recall there is some way to get 80 Bit Extended on 64 Bit Windows, but it involved compiling a 64bit version of FPC myself somehow, and I can't remember what it was all about, I'm pretty sure I was doing that for a while, but then I wanted to upgrade and couldn't remember how it was all done, so I went back to Win32, just to get 80 Bit Extended. It's something to do with the cross compiler to 64 bit makes extended a double on 64bit, but if you weren't cross compiling and had just a native 64bit compiler then Extended is 80 bits again.

James

Reply by email

Marco van de Voort
to Thomas Kurz: That's another thing I've never understood. How can it depend on the OS? It's the CPU…

Op 22-2-2024 om 15:08 schreef Thomas Kurz via fpc-pascal:

> If you're using Win64, then the answer is simple: x86_64-win64 unlike any > other x86 target does not support Extended, so neither the compiler nor the > code in runtime will ever calculate anything with that precision. That's another thing I've never understood. How can it depend on the OS? It's the CPU which does math, and I don't understand what the OS has to do with that? If amd64 architecture didn't support the extended-type at all, I'd say "ok". But it's supported on Linux but not on Windows? Huh?

The problem is not that it is only Extended that is deprecated on win64, but the whole of x87. To replace it, the Windows 64-bit ABI points to SSE2 floating point math which only goes up to 64-bit Double.

I.e. it is not that Microsoft might not in time save the few extra bits of an extended in a x87 context save, but more that it won't save the x87 state at all, and only save the SSE2 state.

Reply by email