5

簡単なプログラミング演習に取り組んでいるときに、実変数が正確な値に達したときに終了することを意図した while ループ (Fortran では DO ループ) を作成しました。

精度が使用されているため、等式が満たされず、ループが無限になることに気付きました。もちろん、これは前例のないことではなく、2 つの数値が等しいかどうかを比較するのではなく、2 つの数値の絶対差が設定されたしきい値よりも小さいかどうかを確認することをお勧めします。

残念だったのは、変数が倍精度であっても、ループが適切に終了するために、このしきい値をどれだけ低く設定しなければならなかったかということです。さらに、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_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;
}

Fortran のコメントアウトされた行は、ループが正常に終了するために使用する必要があるものです。しきい値が 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

精度が悪ければ、Fortran の速度と並列化の容易さは何の役に立つでしょうか? 物事を行う3つの方法を思い出します:

  1. 正しい道

  2. 間違った方法

  3. マックスパワーウェイ

「やり方が間違っているだけじゃないの?」

「うん!でももっと早く!」

冗談はさておき、私は何か間違ったことをしているに違いありません。

Fortran は、他の言語と比較して数値の精度に固有の制限がありますか、それとも (かなりの確率で) 私に問題があるのでしょうか?

私のコンパイラは gfortran (gcc バージョン 4.1.2)、Perl v5.12.1、デュアル コア AMD Opteron @ 1 GHZ です。

4

2 に答える 2

15

あなたの割り当ては、誤って値を単精度に変換してから倍精度に戻しています。

試してみると、問題が修正されている0.1 *こと0.1D0 *がわかります。

于 2010-07-26T02:27:18.717 に答える
8

すでに回答したように、Fortranの「プレーン」浮動小数点定数は、デフォルトでデフォルトの実数型になります。これは単精度である可能性があります。これはほとんど古典的な間違いです。

また、「kind = 8」の使用は移植性がありません。gfortranでは倍精度が得られますが、他のコンパイラでは得られません。Fortran> = 90で変数と定数の両方の精度を指定するための安全で移植可能な方法は、組み込み関数を使用して、必要な精度を要求することです。次に、精度が重要な定数に「種類」を指定します。便利な方法は、独自のシンボルを定義することです。例えば:

integer, parameter :: DR_K = selected_real_kind (14)

REAL(DR_K), PARAMETER :: MACH_2_METERS_PER_SEC = 340.0_DR_K

real (DR_K) :: mass, velocity, energy

energy = 0.5_DR_K * mass * velocity**2

これは、整数の場合、たとえば大きな値が必要な場合にも重要になる可能性があります。整数に関する関連する質問については、Fortran:integer * 4 vs integer(4)vs integer(kind = 4)およびFortranのLongintsを参照してください。

于 2010-07-26T03:39:07.820 に答える