Отличия стандарта 2023 года от действовавшего до него стандарта Fortran 2018 полностью описаны в свободно доступном документе The new features of Fortran 2023, не имеющем официального статуса.
Приведём короткий обзор нововведений.
Максимальная длина строки программы увеличена до 10 тысяч символов, максимальная длина одного оператора – до миллиона символов. Компиляторам предписывается в точности соблюдать эти лимиты, не более и не менее. Как утверждается, целью является облегчение написания программ искусственными интеллектами.
Разрешено автоматическое размещение в памяти строк переменной длины в результате получения выходных параметров системных процедур и операторов (т.е. пользователю не надо руками отводить буфер).
Новые атрибуты typeof и classof для создания переменных такого же типа, как другие:
integer :: i
typeof (i) :: j
Долго ожидавшиеся условные выражения:
value = ( a>0.0 ? a : 0.0)
Скобки обязательны, но одно выражение может содержать вложенные условия:
value = ( a>0.0 ? a : b > 0.0 ? b : 0.0)
Также результатом условного выражения может быть один или несколько фактических параметров процедуры, в том числе передаваемых по ссылке или опускаемых необязательных:
call sub ( ( x>0? x : y>0? y : z ), ( edge>0? edge : mode==3? 1.0 : .nil.) )
Лексема .nil. здесь передаёт отсутствие фактического параметра.
Более расширенное использование двоичных, восьмеричных и шестнадцатеричных (boz) литеральных констант. В языке Фортран такие литералы не имеют типа, представляя собой просто запись содержимого ячеек памяти, поэтому их использование отличается от использования десятичных чисел. В целом, теперь, когда можно вывести тип значения по левой части присваивания, то в правой могут использоваться boz.
Добавлены полезные подпрограммы токенизации строк split и tokenize для эффективной работы со словами в строке.
Через 59 лет после реализации аналогичного решения в языке PL/I, добавлены тригонометрические функции для работы с аргументами в градусах (acosd вдобавок к acos и так далее до tand).
Добавлены тригонометрические функции для работы с аргументами в интервале от 0 до Пи в соответствии со стандартом IEEE (от acospi до tanpi).
Функция selected_logical_kind позволяет получить атрибут байтовой длины логического значения, в которое уместится заданная разрядность.
Ограничены типы параметров стандартной подпрограммы system_clock. В частности, разрядность должна быть не меньше умолчательного размера integer.
Добавлены новые функции педантичных максимумов и минимумов для соответствия новому стандарту IEEE, ISO/IEC 60559:2020 – ieee_max, ieee_max_mag, ieee_min, ieee_min_mag. Отличаются от обычных точной спецификацией, что возвращается в случае различных NaN'ов, положительных и отрицательных нулей и т.п.
Добавлены целые константы logical8, logical16, logical32,
logical64, real16 для указания размеров соответствующих типов. Заметим, что во всех вменяемых компиляторах они равны 8, 16, 32, 64 и 16 соответственно. Шиза косит наши ряды.
Расширено взаимодействие с функциями на языке Си всякими экзотическими случаями вроде многобайтовой кодировки символов по умолчанию.
Добавлены следующие два крайне полезных нововведения в форматном выводе, которых (во всяком случае, второго) ждали чуть ли не 70 лет.
Новый формат вывода AT действует как A с применением функции trim, то есть отбрасывает лишние хвостовые пробелы:
print "(AT,AT)", "Imperial ", "bastion"
Imperialbastion
Наконец!!! можно управлять печатью нуля перед десятичной точкой в вещественных числах между 0 и 1 (ранее могло быть как .5, так и 0.5 в зависимости от реализации). Для этого предназначены управляющие форматы LZP, LZS, LZ (print/suppress/default), либо ключевой параметр leading_zero=… в операторе open со значениями print, suppress, processor_defined (по некоторым сведениям – и в операторе write):
print "(LZP, F0.2, 1X, LZS, F0.2)", .5, .5
0.50 .50
Расширен синтаксис оператора namelist.
Разрешены динамически размещаемые в памяти объекты, содержащие в себе комассивы, и всё это очень сложно работает.
Добавлен механизм put with notify, обеспечивающий изменение данных в чужом адресном пространстве с отправкой уведомления. Для этого расширен синтаксис оператора комассивного присваивания опцией notify и добавлен оператор notify wait:
use iso_fortran_env
type(notify_type) nx[*]
me = this_image()
if (me <= 4) then
x(me)[10, notify=nx] = y
else if (me == 10) then
notify wait (nx, until_count=4)
z(1:4) = x(1:4)
end if
Чем это лучше последовательных операторов присваивания и нотификации? Тем, что для двух разных операторов последовательность сообщений об изменении данных и о нотификации может перепутаться в пути от одного узла к другому, и фокус не удастся.
Улучшена обработка ошибочных состояний в тех случаях, когда они возникают только в части параллельных потоков.
Добавлены простые (simple) процедуры. Они отличаются от чистых (pure) процедур тем, что не только не модифицируют внешнее окружение иначе, чем через формальные параметры, но и не читают внешнее окружение иначе, чем через формальные параметры. Очевидно, полезно для выгрузки кода в GPU и в прочих неоднородных архитектурах.
Массивы разрешено индексировать массивами, задавать размерность (количество измерений) массива динамически, и вообще много разврата в стиле языка APL:
A(@[3,5])
! Array element, equivalent to A(3, 5)
A(6, @[3,5], 1)
! Array element, equivalent to A(6, 3, 5, 1)
A(@V1, :, @V2)
! Rank-one array section, the rank of A being
! SIZE (V1) + 1 + SIZE (V2).
integer, dimension(3) :: lb_array = 0
real :: zz(lb_array+2:)
real, dimension(lb_array:) :: x, y
real, allocatable, dimension(:,:,:) :: x, y, z
integer :: lower(3), upper(3)
allocate(x(:upper), y(lower:upper), z(0:upper))
subroutine ex(a)
real, rank(2) :: a
! Equivalent to real :: a(:,:)
integer :: x0(10,10,10)
logical, rank(rank(x0)), allocatable :: x1
! rank 3, deferred shape
complex, rank(2), pointer :: x2
! rank 2, deferred-shape
logical, rank(rank(x0)) :: x3
! rank 3, assumed-shape
real, rank(0) :: x4
! scalar
Долгожданное введение спецификации редукции в асинхронный оператор цикла, которое раньше было возможно только в макросах OpenMP:
real :: a, b, x(n)
a = 0.
b = -huge(b)
do concurrent (i = 1:n) reduce(+:a) reduce(max:b)
a = a + x(i)**2
b = max(b,x(i))
end do
Наконец, много малополезных возможностей, связанных с enum.
В целом, стандарт Fortran 2023 года производит положительное впечатление, а отдельных нововведений прямо-таки заждались. Но при этом вызывает беспокойство, что комитет ISO сильновато ушёл в отрыв от разработчиков компиляторов: предыдущий стандарт Fortran 2018 поддержан компиляторами только в небольшой части, а стандартом де-факто на сегодняшний день остаётся Fortran 2008. Остаётся надеяться, что хотя бы самые востребованные возможности Fortran 2023 будут реализованы достаточно быстро.
Комментарии (16)
ImagineTables
16.01.2024 11:37+2Выбросы энергии в ноосферу говорят: кто-то опять применил тёмное искусство некромантии!
boulder
16.01.2024 11:37+1То есть у языка ФОРмул до 2024 года не было условных выражений?
Здорово, да :)mobi
16.01.2024 11:37+2Было
merge(a, 0.0, a>0.0)
, а теперь в качестве альтернативы добавили привычный по другим языкам синтаксис(a>0.0 ? a : 0.0)
.vadimr Автор
16.01.2024 11:37Не совсем так, merge – обычная функция, а ? - специальная форма. В первом случае вычисляются значения всех трёх аргументов, поэтому такая, например, штука не пройдёт:
merge(a(i), 0.0, i >= 1)
mobi
16.01.2024 11:37Если функция
a
объявлена какpure
, т.е. не имеет побочных эффектов, то уже в режиме-O1
компилятор gfortran не будет вызывать ее без необходимости:
https://godbolt.org/z/cKcdMrdxP
А иначе, да, стандарт требует вычисления всех аргументов функции.
Но в большинстве случаев gfortran (в отличие от ifort и ifx, как ни парадоксально) очень хорошо оптимизируетmerge
.vadimr Автор
16.01.2024 11:37Имелся в виду массив.
mobi
16.01.2024 11:37А, тогда тем более будет оптимизировано.
vadimr Автор
16.01.2024 11:37+1program m implicit none real :: a(2) = 0. integer :: i = 0 print *, merge(a(i), 0.0, i>=1) end program m
gfortran merge.f90 -omerge -O3 -Wl,-ld_classic -ftree-vectorize -march=native -flto -fcheck=bounds
./merge At line 8 of file merge.f90 Fortran runtime error: Index '0' of dimension 1 of array 'a' below lower bound of 1 Error termination. Backtrace: #0 0x10b38aaae #1 0x10b38b7a5 #2 0x10b38bd15 #3 0x10aa72eb9 #4 0x10aa72ede #5 0x7ff80bba9385
Ну и вообще нельзя полагаться на оптимизацию в принципиальном вопросе.
mobi
16.01.2024 11:37Ну так вы же специально указали
-fcheck=bounds
. Без него (просто-O3
) у меня gfortran 13.2 выводит0.00000000E+00
без каких-либо ошибок.Да и не понимаю я, о чем мы спорим. Я просто привел пример, каким был аналог тернарного условного оператора в предыдущих версиях Fortran.
vadimr Автор
16.01.2024 11:37Без check программа просто тихо лезет в чужую память, что не очень хорошо и не всегда возможно.
Да и не понимаю я, о чем мы спорим. Я просто привел пример, каким был аналог тернарного условного оператора в предыдущих версиях Fortran.
А я уточнил, что это не совсем аналог.
mobi
16.01.2024 11:37Там по ассемблерному коду видно, что компилятор оптимизирует до просто
print *, 0.0
и никуда не лезет. И даже в более сложных случаях, если нет побочных эффектов,merge
заменяется на сравнение и условный переход, т.е. полностью аналогично тернарному условному оператору.vadimr Автор
16.01.2024 11:37+1Условная операция, будучи специальной формой, не вычисляет неактивную ветку, это написано в её определении. Любая функция по своей семантике вычисляет все свои входные аргументы, а merge формально является обычной функцией. Иногда оптимизатор может убрать лишнюю работу, но это не гарантированное поведение, и программа не должна от него зависеть. Это важная семантическая разница.
В SICP прямо даётся такой пример. Там бесконечную рекурсию так забабахали, заменив условную специальную форму на условную функцию.
Oangai
Фортран это всегда круто, как стимпанк и альтернативная история...
WinPooh73
Так и представляется реализация Фортрана для механической разностной машины из романа Стерлинга и Гибсона.
Deosis
Это уже какой-то ретрокиберпанк, в котором хакеры охотятся за секретной перфокартой с секретным кодом на Фортране.
Fedorkov
Или как изучать немецкий после английского.