簡體   English   中英

為什么同一個程序的 c 和 fortran 版本會產生不同的結果?

[英]Why the c and fortran versions of this same program produce different results?

我用的是gcc(Ubuntu 9.3.0-17ubuntu1~20.04)9.3.0

c 代碼為:

// Compile with:
//  gcc -o little_c little.c
#include <stdio.h>  // printf

void main(void) {
    int n = 800;
    float a[n][n], b[n][n], c[n][n];
    int i, j, k;

    for (i = 0; i < n; i++) {
        for (j = 0; j < n; j++) {
            a[i][j] = (float) (i+j);
            b[i][j] = (float) (i-j);
        }
    }

    for (i = 0; i < n; i++) {
        for (j = 0; j < n; j++) {
            float t = (float) 0.0;
            for (k = 0; k < n; k++)
                t += a[i][k] * a[i][k] + b[k][j] * b[k][j];
                //t += a[i][k] + b[k][j]; // If I comment the above line and uncomment this, the c and fortran reults are the same
            c[i][j] = t;
        }
    }
    printf("%f", c[n-1][n-1]); // prints the very last element

}

Fortran 代碼:

! Compile with:
!  gfortran -o little_fort little.f90 

program little

implicit none

integer, parameter  :: n = 800
real                :: a(n,n), b(n,n), c(n,n)
real                :: t
integer             :: i, j, k  ! Counters

do i = 1, n
    do j = 1, n
        a(i,j) = real(i-1+j-1)      ! Minus one, for it to be like the c version
        b(i,j) = real(i-1-(j-1))    ! because in c, the index goes from 0 to n-1
    end do
end do

do i = 1, n
    do j = 1, n
        t = 0.0
        do k = 1, n
            t = t + a(i,k) * a(i,k) + b(k,j) * b(k,j)
            !t = t + a(i,k) + b(k,j) ! If I comment the above line and uncomment this, the c and fortran reults are the same
        end do
        c(i,j) = t
    end do
end do
    
write (*,"(F20.4)") c(n,n)  ! This is the same as c[n-1][n-1] in c


end program little

c 程序打印:1362136192.000000

和 Fortran 程序打印:1362137216.0000

如果我不將每個元素乘以自身,就像我在代碼注釋中的 state 一樣,我會為程序的兩個版本得到相同的值:

c 程序:639200.000000

Fortran 程序:639200.0000

為什么當我使用乘法時 c 和 Fortran 代碼產生不同的結果?。 是否必須使用不同的實數實現方式?

差異是由於計算順序和浮點類型的有限精度造成的。

如果將 Fortran 版本更改為

t = t + (a(i,k) * a(i,k) + b(k,j) * b(k,j))

即在帶有ab的術語周圍添加括號,兩種語言的結果相同。 由於使用了+=賦值運算符,C 版本已使用此評估順序。

如評論中所述,這是在可用精度限制下的預期行為。

當我編寫程序的 Ada 版本時,我發現我必須將小數精度降低到 6 位小數才能獲得 Fortran 的答案。

Ada 版本是:

與 Ada.Text_IO; 使用 Ada.Text_Io;

procedure Main is
   type Real is digits 6;
   package Real_IO is new Ada.Text_IO.Float_IO(Real);
   use Real_IO;
   
   subtype Index is integer range 1..800;
   type Matrix is array(Index, Index) of Real;
   
   A : Matrix;
   B : Matrix;
   C : Matrix;
   T : Real := 0.0;
begin
   for I in Index loop
      for J in Index loop
         A(I,J) := Real(I - 1 + J - 1);
         B(I,J) := Real(I - 1 - (J - 1));
      end loop;
   end loop;
   
   for I in Index loop
      for J in Index loop
         T := 0.0;
         for K in Index loop
            T := T + A(I,K) * A(I,K) + B(K,J) *B(K,J);
         end loop;
         C(I,J) := T;
      end loop;
   end loop;
   
   Put(Item => C(Index'Last, Index'Last), Exp => 0, Aft => 4);
   New_Line;
end Main;

行定義類型 Real 定義了浮點類型的精度:

type Real is digits 6;

使用六位精度產生的值是

1362137216.0000

使用更高精度的浮點類型導致值

1362135200.0000

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM