Работая над простым упражнением по программированию, я создал цикл while (цикл DO в Фортране), который должен был завершиться, когда реальная переменная достигла точного значения.
Я заметил, что из-за используемой точности равенство никогда не соблюдалось, и цикл стал бесконечным. Это, конечно, не является чем-то неслыханным, и рекомендуется не сравнивать два числа на предмет равенства, а лучше проверить, меньше ли абсолютная разница между двумя числами установленного порога.
Что меня разочаровало, так это то, насколько низко мне пришлось установить этот порог, даже с переменными с двойной точностью, для правильного выхода из цикла. Более того, когда я переписал «дистиллированную» версию этого цикла на Perl, у меня не было проблем с числовой точностью, и цикл завершился нормально.
Поскольку код, вызывающий проблему, настолько мал как в Perl, так и в Fortran, я хотел бы воспроизвести его здесь, на случай, если я упущу важную деталь:
Код 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#! /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;
}
METERS_PER_SEC = 340.0
velocity = 0.5 * MACH#! /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;
}
METERS_PER_SEC ! Initial Velocity
DO
WRITE (*, 300) velocity
300 FORMAT (F20.8)
IF (count == 50) EXIT
IF (velocity == 5.0 * MACH#! /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;
}
METERS_PER_SEC) EXIT
! IF (abs(velocity - (5.0 * MACH#! /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;
}
METERS_PER_SEC)) < 1E-4) EXIT
velocity = velocity + 0.1 * MACH#! /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;
}
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 ГГц.
0.0144
). Поэтому я бы сказал, что это, вероятно, неправильный ваш метод тестирования. - person Wolph   schedule 26.07.2010