Derived type declaration in array constructor

And for 60 years the results from those codes have been consistently wrong.

I think you are missing my point.

Try this program.

Program ar

  USE ISO_FORTRAN_ENV, ONLY: WP=>REAL64

  Implicit NONE

  Real(WP) :: a(4), b(4), c(4)

  a = [Real(WP) :: 1, 2, 3.14, 4]
  b = [1.0_WP, 2.0_WP, 3.14_WP, 4.0_WP]
  c = [Real(WP) :: 1, 2, 3.14_WP, 4]

  Print *,' a = ', a
  Print *,' b = ', b
  Print *,' c = ', c

  Stop

End Program ar

On my Linux system I get the following results for ifx, gfortran-13 and nvfortran

ifx

  a =    1.00000000000000        2.00000000000000        3.14000010490417
   4.00000000000000
  b =    1.00000000000000        2.00000000000000        3.14000000000000
   4.00000000000000
  c =    1.00000000000000        2.00000000000000        3.14000000000000
   4.00000000000000

gfortran-13

  a =    1.0000000000000000        2.0000000000000000        3.1400001049041748        4.0000000000000000
  b =    1.0000000000000000        2.0000000000000000        3.1400000000000001        4.0000000000000000
  c =    1.0000000000000000        2.0000000000000000        3.1400000000000001        4.0000000000000000

nvfortran

  a =     1.000000000000000         2.000000000000000
    3.140000104904175         4.000000000000000
  b =     1.000000000000000         2.000000000000000
    3.140000000000000         4.000000000000000
  c =     1.000000000000000         2.000000000000000
    3.140000000000000         4.000000000000000

If I want 3.14 to be exactly 3.14 I’m left with doing either b or c. My point is using the type spec as in a I should not have to append _WP to 3.14 to get exactly 3.14. To me this is a flaw in the language irrespective if its been that way for 60 years. Say you have a large table of 100 values that you wish to save as a REAL(REAL64) array. Why should I have to append _WP on each of the 100 values. I’ve tried doing this with something like

Real(REAL64), Parameter :: a(100) = REAL([1,2.4,6.....etc],WP)

but that also can get to be a little unwieldy. Using the type-spec as in a and getting exactly the precision you specify in terms of number of decimal digits should not be a big thing to implement and since the [type-spec :: ac-values] form of the array constructor is a new feature in the language (if you consider something 20 years old new) it should not effect legacy code.

We had this discussion many times…

It’s not a flaw in the langage, it’s a “by design” property of the floating point model, which can NOT represent all the (mathematical) real values. And even 3.14_real64 is not exactly 3.14:

use iso_fortran_env
real(real128) :: x = 3.14_real64
print "(F30.20)", x
end

Prints: 3.14000000000000012434

I would argue that if you have to enter 100 decimal values with only a few significant digits in the fraction part, while needing a 15 digits actual precision, then the flaw is probably in the values themselves.

And in what alternate reality are you living. Even if a value has only a few decimal places of accuracy, you still want them to have a consistent precision as the rest of the variables in your code even if they don’t need the entire 15 or 16 digits of precision. You see a lot of tabular data in books reports etc where the number of decimal places given is variable. One value might have only two decimals shown but the next value in the table might have 8. I agree its “design” choice but in my opinion its a poor one that continues to plague the language. I’m not asking for a total rewrite of the entire floating point system. Only how literal constants are treated in initialization or assignment statements.

If a value in a table is given with 3 digits, e.g. 0.123, it’s very likely because its actual accuracy is 10^{-3}. If you enter it as 32bits real literal 0.123 then you introduce an additional 10^{-7} relative error, which is completely negligible.

The real trap here is if you have to to enter a value given more more than 7 digits, which are all significant, e.g. 0.123456789 with a 10^{-9} accuracy. The 10^{-7} error if entered as a 32 bits real literal is then unacceptable, and you should enter it as 0.123456789_real64 to have a 10^{-15} error.

Appending _wp to values having 10 digits is not a big deal.

The current convention is that the value is independent of the context. It does not depend if the literal is in an expression, or in an assignment, or as an actual argument to a subprogram, or in an initialization, the value is the same in all those contexts. That is a simple rule for humans to remember, and although it does sometimes take a few characters extra of typing, it does not impose a huge burden on the programmer. If the value instead depended on the context (say assignments are different from expressions, etc.) then the programmer would need to master a whole new level of complications.

There is existing already one context where a “literal” does depend on context, and that is with formatted i/o. If the “literal” is read into different floating point variables with different kinds, then the resulting value depends on those kinds. It is, of course, impossible to specify the kind value of the “literal” in this case, so it must, by default, depend on the kind value of the variable. In this case, specifying, e.g. 1.234e0 and 1.234d0 result in the same values. Here is an example of this.

program xxx
   use, intrinsic :: iso_fortran_env, only: real16, real32, real64, real128
   character(3) :: cval='1.1'
   character(*), parameter :: cfmt='(i2,2f40.36)'
   !real(real16) :: a16, r16
   real(real32) :: a32, r32
   real(real64) :: a64, r64
   real(real128) :: a128, r128
   !a16=1.1; read(cval,*) r16; write(*,cfmt) precision(r16), a16, r16
   a32=1.1; read(cval,*) r32; write(*,cfmt) precision(r32), a32, r32
   a64=1.1; read(cval,*) r64; write(*,cfmt) precision(r64), a64, r64
   a128=1.1; read(cval,*) r128; write(*,cfmt) precision(r128), a128, r128
end program xxx
$ gfortran xxx.f90 && a.out
 6  1.100000023841857910156250000000000000  1.100000023841857910156250000000000000
15  1.100000023841857910156250000000000000  1.100000000000000088817841970012523234
33  1.100000023841857910156250000000000000  1.100000000000000000000000000000000077

If you use flang instead of gfortran, then you can uncomment the a16 lines but you must comment out the a128 lines. Note how all the assigned values are the same, but the values read with i/o are all different.

edit: I changed the code a little to more clearly demonstrate that all the assigned values are the same.

In this case this is a character literal, not a numeric literal. So it does not really break the rule.

This is true, that is why I put the word “literal” in quotes. This is as close as I know within fortran how to simulate the “infinite precison” behavior of literals that is often proposed in these discussions. In this case, the i/o library knows what is the kind of the target variable, so it can keep converting the decimal integers to bits until the target precision is attained (in practice, the last bit is sometimes rounded incorrectly, but that is a general feature of floating point, not of fortran). But as I said previously, that does not work in the other contexts, such as subprogram arguments and within expressions, so it cannot be done in a simple consistent way elsewhere within the language. All of these other situations would need their own set of conversion rules, and the programmer would need to learn that new level of complexity. Instead, the current fortran rule in all of these other (non i/o) cases is simple for the programmer: namely the value depends on the specified kind, it does not depend on context, and it is always the same.

This rule has changed over the life of the language. Prior to f90, there was some ambiguity in the language standard how, for example, literals in data statements were converted. This caused portability issues as different compilers adopted different conventions, and it resulted in inconsistencies even within some compilers, and this issue was eventually resolved to this current convention. There are also some ambiguities in the language standard about how expressions are evaluated that is closely related to this issue, but still distinct. For example, the compiler is allowed to use 80-bit registers to evaluate floating point expressions where the results can differ from the evaluations performed with 32-bit or 64-bit precision. Another example, the compiler is allowed to use fused multiply-add instructions where the results can differ from the combination of separate multiply and add instructions, or it can use simd vector instructions to evaluate blocks of operations that differ from the sequence of scalar operations. These differences are small, usually from different rounding conventions in the instruction or from different treatments of denormalized values. In contrast, treating literals as if they they had infinite precision or as if they had different kind values results typically in larger differences in the computed results.

I also should mention that the NAG compiler supports both the a16 and the a128 lines in the above code. In this case, the a16 value differs from the other assigned values because it cannot represent exactly the real32 value on the rhs of the statement. There are, of course, some decimal values that can be represented exactly by all floating point kinds, but 1.1 is not one of them.

The issue is now fixed in LFortran. Here is the error message:

$ lfortran a.f90
semantic error: Invalid syntax of derived type for array constructor
  --> a.f90:13:17
   |
13 |         CALL s([TYPE(tp) :: v1,v2,v3]) ! incorrect
   |                 ^^^^^^^^ help: use just the derived type name 'tp', without the keyword 'type'

If you just use tp, then it works.

Thank you Ondřej. But this has an interesting side-effect. Consider the little mischief:

PROGRAM real_as_rw

        TYPE real
           INTEGER(8) :: sf    ! Scaled fraction represetnation of a real
        END TYPE real

        TYPE (real) r1,r2,r3

        REAL(8) :: scale = 2.0D0**31

        r1%sf = scale*1.0D0
        r2%sf = scale*2.0D0
        r3%sf = scale*3.0D0

        CALL s1([real :: r1,r2,r3])

        CONTAINS

        SUBROUTINE s1(ra)
        TYPE(real) :: ra(3)
        WRITE(*,*)ra
        END SUBROUTINE s1

END PROGRAM real_as_rw ! *****************************************************************

This, very reasonably, won’t compile, e.g. gfortran (GNU Fortran (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0)
7 | TYPE real
| 1
Error: Type name ‘real’ at (1) cannot be the same as an intrinsic type

But this means that there are no reserved words in Fortran EXCEPT that you can’t have a derived type called REAL (or LOGICAL, COMPLEX, CHARACTER) - i.e. these are reserved words in some contexts. As a compiler writer I am aware that Fortran is a maze of special cases, but I had missed this one. I can assume that the set of intrinsic types is complete - we won’t make c-strings, c-pointers or anything else in the future (pity - I like the idea of CHARM and STRANGENESS).

What other reserved words are lurking in the shadows?

It’s actually the first constraint for the derived-type-stmt:

C734(R727) A derived type type-name shall not be DOUBLEPRECISION or the same as the name of any intrinsic type defined in this document.

I think it’s mostly related to one of the declaration-type-spec cases being TYPE(intrinsic-type-spec). I’d never do that, but maybe it makes code generation easier?

You can still do

type(real) :: real

(and disable access to the real intrinsic function in the process.)

I hope it’s not. We still need a proper container (that would fix the issue with arrays of allocatable strings), a dictionary/map, revisit BITS, formalize UNSIGNED, etc.