2

Fortran から C++ への移植を完了しましたが、COMPLEX 型にいくつかの違いがあることを発見しました。次のコードを検討してください。

PROGRAM CMPLX
    COMPLEX*16 c
    REAL*8 a
    c = (1.23456789, 3.45678901)
    a = AIMAG(1.0 / c)
    WRITE (*, *) a
END

そしてC++:

#include <complex>
#include <iostream>
#include <iomanip>

int main()
{
    std::complex<double> c(1.23456789, 3.45678901);
    double a = (1.0 / c).imag();

    std::cout << std::setprecision(15) << " " << a << std::endl;
}

C++ バージョンを clang++ または g++ でコンパイルすると、次の出力が得られます: -0.256561150444368 ただし、Fortran バージョンをコンパイルすると、-0.25656115049876993 が得られます。

つまり、どちらの言語も IEEE 754 に従っているのではないですか? Octave(Matlab)で次を実行すると:

octave:1> c=1.23456789+ 3.45678901i
c =  1.2346 + 3.4568i
octave:2> c
c =  1.2346 + 3.4568i
octave:3> output_precision(15)
octave:4> c
c =  1.23456789000000e+00 + 3.45678901000000e+00i
octave:5> 1 / c
ans =  9.16290109820952e-02 - 2.56561150444368e-01i

C++ バージョンと同じ結果が得られます。Fortran の COMPLEX 型はどうなっていますか? いくつかのコンパイラ フラグがありませんか? -ffast-math は何も変更しません。C++ と Fortran でまったく同じ 15 の 10 進数を生成したいので、移植の違いを簡単に見つけることができます。

Fortran の専門家はいますか? ありがとう!

4

2 に答える 2

5

Fortran コードで置き換えます

c = (1.23456789, 3.45678901)

c = (1.23456789d0, 3.45678901d0)

なしでkindは、rhs で使用する実数リテラルは、ほとんどの場合、32 ビット実数であり、おそらく 64 ビット実数が必要です。サフィックスd0により、コンパイラは、指定した値に最も近い 64 ビット実数を作成します。私はこれでいくつかの詳細をざっと説明しました. 実数リテラルの種類を指定する他の (おそらくもっと良い) 方法がありますが、このアプローチは現在のどの Fortran コンパイラでも問題なく動作するはずです.

私は C++ をよく知りません。C++ コードに同じ問題があるかどうかはわかりません。

あなたの質問を正しく読んだ場合、2 つのコードは単精度の限界である 8sf に対して同じ答えを生成します。

IEEE-754 への準拠に関しては、私が知る限り、その標準は複雑な演算の問題をカバーしていません。舞台裏で使用される fp 演算は、ほとんどの場合、予想される誤差範囲内で複素数の結果を生成することを期待していますが、fp 演算の誤差範囲がそうであるように、それらが保証されていることを認識していません。

于 2015-01-12T19:36:00.570 に答える
0

すべての Fortran 定数を DP に変更することを提案します

1.23456789_8 (または 1.23456789D00) など

AIMAG の代わりに DIMAG を使用する

于 2015-01-12T19:34:45.790 に答える