简体   繁体   English

为什么同一个程序的 c 和 fortran 版本会产生不同的结果?

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

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

The c code is: 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 code: 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

The c program prints: 1362136192.000000 c 程序打印:1362136192.000000

and the Fortran program prints: 1362137216.0000和 Fortran 程序打印:1362137216.0000

If I do not multiply each element by itself, as I state in the comments in the code, I get the same value for both versions of the program:如果我不将每个元素乘以自身,就像我在代码注释中的 state 一样,我会为程序的两个版本得到相同的值:

c prigram: 639200.000000 c 程序:639200.000000

Fortran program: 639200.0000 Fortran 程序:639200.0000

Why when I use a multiplication the c and Fortran code produce different results?.为什么当我使用乘法时 c 和 Fortran 代码产生不同的结果?。 Does it have to be with different implementations of the real numbers?是否必须使用不同的实数实现方式?

The difference is due to the order of evaluation combined with the limited precision of the floating point type.差异是由于计算顺序和浮点类型的有限精度造成的。

If you change the Fortran version to如果将 Fortran 版本更改为

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

ie add parenthesis around the terms with a and b , you get the same result for both languages.即在带有ab的术语周围添加括号,两种语言的结果相同。 The C version already uses this order of evaluation due to the use of the += assignment operator.由于使用了+=赋值运算符,C 版本已使用此评估顺序。

As mentioned in the comments, this is expected behavior at the limits of the available precision.如评论中所述,这是在可用精度限制下的预期行为。

When I wrote an Ada version of the program I found that I had to reduce the decimal precision to 6 decimals to achieve the Fortran answer.当我编写程序的 Ada 版本时,我发现我必须将小数精度降低到 6 位小数才能获得 Fortran 的答案。

The Ada version is: Ada 版本是:

with Ada.Text_IO;与 Ada.Text_IO; use 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;

The line defining type Real defines the precision of the floating point type:行定义类型 Real 定义了浮点类型的精度:

type Real is digits 6;

The value produced using six digits of precision is使用六位精度产生的值是

1362137216.0000

Use of higher precision floating point types resulted in the value使用更高精度的浮点类型导致值

1362135200.0000

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM