Open zerothi opened 3 years ago
This is indeed one of the rather annoying and nasty surprises of Fortran's floating point handling. So, I for one would really appreciate a feature like this.
I think the most irritating issue with the current behaviour is that
real(8), parameter :: const2 = 1./3.
yields
0.33333334326744080
At first I would have thought that the value should just be truncated and zero-padded, but I do realise that this would be as wrong as the current value.
A quite nice summary of these issues can be found here: https://www.fortran90.org/src/best-practices.html#floating-point-numbers https://www.fortran90.org/src/gotchas.html#floating-point-numbers-gotcha
Currently, the only reliable way I found to deal with this is via compiler flags, especially if one deals with codes that contain bits and pieces from various sources (as is the case with a lot of Fortran codes especially).
Currently, the only reliable way I found to deal with this is via compiler flags, especially if one deals with codes that contain bits and pieces from various sources (as is the case with a lot of Fortran codes especially).
Yes. That could be one existing approach. However, this would only work if your code is using a consistent precision all around. I.e. mixed precision codes would be difficult to handle through compiler flags, or it could have some performance issues at least. Having this built-in the standard would clarify and make a lot of things easier.
One question is to get this into the standard, where I can see objections about "backwards incompatibility". However, this issue I think can be completely fixed by a compiler that would warn or refuse to compile your first example. I just created an issue for this in LFortran:
https://gitlab.com/lfortran/lfortran/-/issues/305
In general I would like LFortran to have a mode that is "pedantic" and does not allow such code. Similar with "implicit none" if you forget to specify it, it will refuse to compile your code. That way it will force you to write correct code (that will correctly work with other compilers today), without needing to change the standard.
I believe that is the easiest way to get these issues fixed in practice.
One question is to get this into the standard, where I can see objections about "backwards incompatibility"....
I sort of had this feeling that perhaps there could be some backwards incompatibility. However, I think that the backwards incompatibility is a "random" fluctuation of numbers (as shown in the prior example). You never know which numbers gets appended in double precision. Here the clarity is implicit.
However, this issue I think can be completely fixed by a compiler that would warn or refuse to compile your first example. I just created an issue for this in LFortran:
Yeah, compiler flags for checking this would also be awesome -fpedantic-kind
or something similar. have just opened up a "bug" @ gfortran see here
... I believe that is the easiest way to get these issues fixed in practice.
Yeah, you are probably right, but my feeling is that nobody does this by intent. And if so they can easily be explicit. I think this would save more code than it breaks, if it actually will break anything. :)
For gfortran -Wconversion
and -Wconversion-extra
could be used.
KIND inference implicitly from context can cause significant problems in terms of backward compatibility.
The issue is really is with the KIND of REAL (and INTEGER) literal constants and arguments and results of intrinsic subprograms.
A proposal such as #78 will be a good approach for Fortran.
KIND inference implicitly from context can cause significant problems in terms of backward compatibility.
Could you give an example where this would cause significant problems? I have tried to think of one that is really a problem, but I can't come up with anything but "unintended" assignment as mentioned above.
Sometimes legacy codes have strict restrictions on backwards compatibility, i.e. results may have to be reproducible to the last bit. That will not be fulfilled if the precision of the expression in the following example changes
REAL(DP), PARAMETER :: EXAMPLE = 1./3.
Sometimes legacy codes have strict restrictions on backwards compatibility, i.e. results may have to be reproducible to the last bit. That will not be fulfilled if the precision of the expression in the following example changes
REAL(DP), PARAMETER :: EXAMPLE = 1./3.
But here, that is not explicit on what the code wants? If it truly wants bit-set reproduceability, they should do 1._sp / 3._sp
for clarity. Compiling that code with -fdefault-real-8
would also break it.
I would consider the above a bug.
Is this something you want a PR describing?
I think such a proposal would have more cons than pros. The current rule is consistent over the whole standard, i.e. the evaluation of an expression does not depend on the context where it appears. Easy to remember.
If
REAL(DP), PARAMETER :: EXAMPLE = 1./3.
was interpreted as
REAL(DP), PARAMETER :: EXAMPLE = 1._dp/3._dp
then many people would think that
real :: x(n)
real(dp) :: y(n)
y(:) = x(:) / 3.0
should also be interpreted as
y(:) = x(:) / 3.0_dp
and will omit the _dp
, thinking it's implied.
@PierUgit I think probably the best bet is to have good compiler warnings. I personally always append ._sp
or ._dp
and then it's always clear, no surprises. A compiler warning can be a lot more specific, only warn (and thus require ._dp
) if a value is changed to what the user might expect (e.g., unless I am mistaken, I think 3.0
is the same as 3.0_dp
so no warning technically needed, but 3.1
is not the same as 3.1_dp
, so warning needed). As you correctly pointed out, we might need such warnings with this proposal also. In which case perhaps just keeping things as they are, with good warnings, might be enough.
I still don't think that the corner cases should hold back something that is consistently done wrong. And this is a major issue for fortran programs (and new programmers!).
And I don't see any connection between your all constant vs. mixed variables with explicit kind specification. They are very different, since that is intentionally mixing different kinds.
While your example is real, I think the majority of programs tend to do all operations in 1 precision. One can in the standard define the conversion factors as implicit for the highest precision variable in the expression. In your case 3 would be bumped to double.
Another approach would be to say that all constants are doubles (like c).
I still don't think that the corner cases should hold back something that is consistently done wrong. And this is a major issue for fortran programs (and new programmers!).
On the contrary, when considering changing a decades-old behavior one has to carefully browse all the cases that could be affected, including the corner cases. And this is particularly important for the legacy codes (but not only).
Note also that I disagree on the "major issue": yes this is a gotcha for new Fortran programmers, but once you have been caught you learn and you don't do the same mistake again.
I can see another case where an automatic promotion (one of your proposals) would be a problem:
real :: x(n), y(n)
real(dp) :: s
s = 0d0
do i = 1, n
s = s + cos( x(i) * y(i) / 3.0 )
end do
Here I want a double precision accumulator but I don't necessarily want the whole cos(...)
expression to be promoted to double precision, because of performance reasons (vector registers handle twice as less dp variables, and a cos() is longer to compute in double precision. And specifying 3.0_sp
is not a solution for existing codes.
Apart from the suggestion of @certik to enable compiler warnings when detecting for literal constants that may have insufficient precision, the only reasonnable solution I can see would be making mandatory the use of _kindvalue
for all litteral constants if something like
implicit none(litteral_kind)
was present
Note also that I disagree on the "major issue": yes this is a gotcha for new Fortran programmers, but once you have been caught you learn and you don't do the same mistake again.
My experience is not this, quite often I find problems in submitted code that is because people just forget, even though they are capable, and knowledgeable about these issues.
I can see another case where an automatic promotion (one of your proposals) would be a problem:
real :: x(n), y(n) real(dp) :: s s = 0d0 do i = 1, n s = s + cos( x(i) * y(i) / 3.0 ) end do
Here I want a double precision accumulator but I don't necessarily want the whole
cos(...)
expression to be promoted to double precision, because of performance reasons (vector registers handle twice as less dp variables, and a cos() is longer to compute in double precision. And specifying3.0_sp
is not a solution for existing codes.Apart from the suggestion of @certik to enable compiler warnings when detecting for literal constants that may have insufficient precision, the only reasonnable solution I can see would be making mandatory the use of
_kindvalue
for all litteral constants if something likeimplicit none(untyped_litteral)
was present
I agree that a compiler warning is a good thing to do. Sometimes breaking changes needs to be done to make life easier... ;)
Note that Fortran 202Y will actually bring a solution here, by allowing to specify in the code what are the default kinds. See this proposal that has been approved at the june 2023 meeting: https://j3-fortran.org/doc/year/23/23-199r1.txt
Problem
Currently
real
are by definition floating point values. So when assigning constants to a double precision you'll get conversion lossesthe output will be:
to much surprise of some users.
Current way
Users are forced to explicitly denote the important constants as proper kinds. I.e. simple integers or reals that are well defined may not be affected. The solution to the above would be:
note not all are needed. This alters the output to:
Suggestion
One needs to distinguish the two situations.
Consider this:
in both the above cases
1.
and3.
should automatically be bumped to thekind=8
specification as noted in the parameter declaration and by use of onekind=8
Another benefit is that complicated inline math would be much easier to write without worrying about kind-specifications.
Note that all of the same considerations of the above should apply to
integer
andcomplex
data types.Breaking code
I hardly think this would break codes in undesirable ways. In fact, what it would do is ensure correct handling in many codes since the kind specification is not necessary in many cases any more. So results may change, but I would argue that there are no cases where one wants to do less precision math (on purpose) and store in higher precision variables.