Работая над простым упражнением по программированию, я создал цикл while (цикл DO в Фортране), который должен был завершиться, когда реальная переменная достигла точного значения.
Я заметил, что из-за используемой точности, равенство никогда не было достигнуто, и цикл стал бесконечным. Это, конечно, не неслыханно, и рекомендуется, чтобы вместо сравнения двух чисел на равенство лучше было бы увидеть, если абсолютная разница между двумя числами меньше установленного порога.
Что меня разочаровало, так это то, как низко я должен был установить этот порог, даже с переменными с двойной точностью, чтобы мой цикл корректно завершился. Кроме того, когда я переписал «перегнанную» версию этого цикла в Perl, у меня не было проблем с числовой точностью, и цикл вышел нормально.
Поскольку код для создания проблемы настолько мал, как в Perl, так и в Fortran, я хотел бы воспроизвести его здесь на случай, если я замутил важную деталь:
Код Фортрана
PROGRAM precision_test
IMPLICIT NONE
! Data Dictionary
INTEGER :: count = 0 ! Number of times the loop has iterated
REAL(KIND=8) :: velocity
REAL(KIND=8), PARAMETER :: MACH_2_METERS_PER_SEC = 340.0
velocity = 0.5 * MACH_2_METERS_PER_SEC ! Initial Velocity
DO
WRITE (*, 300) velocity
300 FORMAT (F20.8)
IF (count == 50) EXIT
IF (velocity == 5.0 * MACH_2_METERS_PER_SEC) EXIT
! IF (abs(velocity - (5.0 * MACH_2_METERS_PER_SEC)) < 1E-4) EXIT
velocity = velocity + 0.1 * MACH_2_METERS_PER_SEC
count = count + 1
END DO
END PROGRAM precision_test
Код Perl
#! /usr/bin/perl -w
use strict;
my $mach_2_meters_per_sec = 340.0;
my $velocity = 0.5 * $mach_2_meters_per_sec;
while (1) {
printf "%20.8f\n", $velocity;
exit if ($velocity == 5.0 * $mach_2_meters_per_sec);
$velocity = $velocity + 0.1 * $mach_2_meters_per_sec;
}
Закомментированная строка в Фортране - это то, что мне нужно было бы использовать для нормального завершения цикла. Обратите внимание, что пороговое значение установлено на 1E-4, что, на мой взгляд, довольно жалко.
Имена переменных взяты из упражнения по самостоятельному программированию, которое я выполнял, и не имеют никакого отношения.
Намерение состоит в том, что цикл останавливается, когда переменная скорости достигает 1700.
Вот усеченные выводы:
Выход Perl
170.00000000
204.00000000
238.00000000
272.00000000
306.00000000
340.00000000
...
1564.00000000
1598.00000000
1632.00000000
1666.00000000
1700.00000000
Выход Fortran
170.00000000
204.00000051
238.00000101
272.00000152
306.00000203
340.00000253
...
1564.00002077
1598.00002128
1632.00002179
1666.00002229
1700.00002280
Что хорошего в скорости и простоте распараллеливания Фортрана, если его точность воняет? Напоминает мне о трех способах делать вещи:
Правильный путь
Неправильный путь
Путь максимальной мощности
"Разве это не неправильный путь?"
«Да! Но быстрее!»
Шутки в сторону, должно быть, я что-то делаю не так.
Есть ли у Фортрана присущие ограничения численной точности по сравнению с другими языками, или я (вполне вероятно) виноват?
Мой компилятор - gfortran (gcc версии 4.1.2), Perl v5.12.1, на двухъядерном процессоре AMD Opteron @ 1 ГГц.