- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在尝试比较 C 和 Fortran 代码的性能。使用 Leibniz's series 计算圆周率, 我有以下 Fortran 代码
program pi_leibniz
implicit none
integer, parameter :: dp=selected_real_kind(15,307)
integer :: k=0, precision=9
real(dp), parameter :: correct = 0.7853981633974483d0, eps = epsilon(real(1,dp))
real(dp) :: sum = 0.0, delta
character(8) :: fmt
logical, parameter :: explicit = .false.
real :: start, finish
delta = 10.**(-precision-1)*0.25
if (delta<eps) then
delta=eps
precision=14
print *, "Precision specified too high, reverting to double precision (14 digits)"
endif
write(fmt,'(A,I0,A,I0,A)') '(f',precision+2,'.',precision,')'
call cpu_time(start)
do
sum = sum + real((-1)**k,dp)/real(2*k+1,dp)
k = k+1
if (abs(sum-correct)<delta) exit
if (explicit) print fmt, 4.*sum
enddo
call cpu_time(finish)
print fmt, 4.*sum
print '(A,I0,A,I0,A)', "converged in ", k, " iterations with ", precision, " digits precision"
print '(g0,a)', finish-start," s"
end program pi_leibniz
和几乎相同的 C 代码:
#include <stdio.h>
#include <time.h>
#include <float.h>
#include <math.h>
int main(void){
int precision=9;
size_t k=0;
const double correct=0.7853981633974483;
double sum=0.0, delta = 0.25*pow(10.0,-(precision+1));
clock_t start,finish;
double sgn = 1.0;
if (delta < DBL_EPSILON){
delta = DBL_EPSILON;
precision = 14;
printf("Precision specified too high, reverting to double precision (14 digits)\n");
}
start = clock();
for(k=0; fabs(sum-correct) >= delta; k++, sgn=-sgn)
sum += sgn/(2*k+1);
finish = clock();
printf("%.*f\n",precision,4*sum);
printf("converged in %zu iterations with %d digits precision\n",k,precision);
printf("%f s\n",(finish-start)/(double)CLOCKS_PER_SEC);
return 0;
}
我使用 GNU 编译器和 -O2 选项进行编译。编辑:64 位。
Fortran 代码以完全 double 运行,在我的机器上几秒钟内计算出 pi 的前 15 位数字。 C 代码的执行速度甚至比 Fortran 快 8 位小数,在相同的迭代次数中收敛到相同的数字;然而,对于 precision=9
,Fortran 代码在 2.27s/1581043254 次迭代中收敛到 3.141592653,而 C 代码需要 12.9s/9858058108 次迭代(~6x)并且最后一位数字相差 1。随着更高的精度,Fortran 的时间是相同的数量级,而 C 需要大约 2 分钟来计算 pi 的前 11 位数字。
造成差异的原因是什么?我该如何避免拖慢 C 代码的速度?
编辑:我按照@pmg 的建议做了,并更改了 C 代码中的循环,使收敛变得单调:
for(k=0; fabs(sum-correct) > delta; k+=2)
sum += 1.0/(2*k+1) - 1.0/(2*k+3);
虽然这在一定程度上加快了较低精度的收敛速度,但它实际上使 C 程序现在甚至在 precision=8
时基本上挂起(计算时间超过 3 分钟)。
编辑 2:由于以 precision>8
计算会导致整数溢出,看来正确的方法是将 k
声明为 integer(8) Fortran 中的::k
和 C 中的 unsigned long
。通过此修改,Fortran 代码现在几乎与 pi 的 10/11 位的 C 代码完全相同并且似乎“挂起”精度更高。
那么,为什么以前使用本质上不正确的方法仍然得出正确的结果,并且花费相同的时间来计算它是 10 位还是 15 位的圆周率?只是为了好玩,需要 1611454902 次迭代才能“收敛”到 3.14159265358979,这恰好是小数点后 14 位的圆周率。
最佳答案
您的 Fortran 代码不正确。
您可能会使用默认的 32 位整数并使用 HUGE(k)
您会看到 k
可以采用的最大整数值是 2147483647。在这种情况下你会有整数溢出发生在迭代计数和(在此之前)其在 real(2*k+1,dp)
中的评估中。
就像您使用 selected_real_kind
来找到满足您要求的实数种类一样,您也可以使用 should selected_int_kind
来找到合适的整数种类。如果我们信任 C 版本,那么迭代计数可能会达到如此大的数目,以至于 k
应该有种类 selected_int_kind(11)
。
关于用 C 和 Fortran 中的 Leibniz 系列计算 Pi,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57328147/
是的,我知道..,这不是想象的...这是一个真正的 Fortran 问题。 以前的版本是指 Fortran 2003、95、90,甚至 77。 我所说的“向后兼容”是指可以轻松运行为 2008 年以前
我有一个程序,它的变量中有一个值。一旦确定了该值,我想调用另一个程序并使用该变量的值来确定在新程序中的位置。有人知道该怎么做吗? 最佳答案 如果您有 Fortran 2008 编译器,您将拥有标准子例
namelist 是一种有用的 fortran 结构,可以从文件中快速初始化变量。 namelist 有一个名称并包含一组具有已知类型的变量。这使得它类似于 type 结构。 通常情况下,给程序或子例
我正在遍历索引,我正在检查我是否不在第一个循环交互和另一个条件中。如果第一个条件是 .False.,我不想评估第二个条件。 do i = 1, n if ( i /= 1 .and. var(
Fortran 2003 具有用于数组连接的方括号语法,Intel fortran 编译器也支持它。我在这里为矩阵连接写了一个简单的代码: program matrix implicit none r
我正在尝试通过重载类型名称来制作自定义数据类型构造函数。但是,在进行调用时,将调用默认构造函数。我不明白我做错了什么。 这是有问题的代码片段。 module test type, pu
我的最终目标是在 Fortran 中有一个通用的映射函数,即一个接受任意类型 A 的数组和一个 A->B 类型的函数的函数,将此函数应用于给定数组的所有元素并返回一个B 类型的数组。我无法用数组实现它
我正在学习 Fortran,在使用格式编写时发现了一些奇怪的东西(我使用的是 Fortran onlinegdb) Program Hello real, dimension(3,2):: array
Fortran 中的INTERFACE 语句是否使其成为正式实现multiple dispatch 的编程语言? ? (我问是因为所链接的维基百科文章在其看似全面的支持相关范式的示例编程语言列表中并未
我可以使用 Fortran 95 编译器编译 Fortran 90 文件吗? Fortran 95 似乎有很多,但 Fortran 90 没有。 最佳答案 这个可以: NAGWare f95 Comp
嗨,我在 Fortran 中对二维离散化问题强加边界条件时遇到了麻烦。我的离散化网格是一个二维正方形,在 x,y 方向上从 -L 到 L。 我想强加这样的边界条件, 在 x=L 的边界线上,指定了函数
Fortran 是否有与 C assert 等效的标准函数/关键字? ? 我找不到 assert我在Fortran2003标准中提到过。我发现了一些如何使用预处理器的方法,但是在这个 answer建议
我有一系列的作业,使用“;”将它们分配给同一个ike。分开statemnts,但我收到此错误: 1.0;磅(1,9) 1个 错误:(1)处无法分类的陈述 在文件LJ.F90:223中 如果每个语句都在
我正在使用 gfortran -std=f2008。我有一个函数,它返回一个包含可分配数组的派生类型。该函数在返回之前调用allocate()。似乎在分配数组的函数返回之后,数组会自动释放一段时间,并
我制作了这个小型测试程序来“证明”在编译之前(或者如果你让它们可分配),你不能在不指定它们的大小的情况下使用向量。我的观点失败了。我期待本地向量“num”会失败。程序在执行程序之前无法知道它的大小。大
出于优化原因,Fortran 强制子例程或函数的虚拟参数不是别名,即它们不指向相同的内存位置。 我想知道相同的约束是否适用于函数的返回值。 换句话说,对于给定的 myfunc 函数: function
我已经在Fortran 90中编写了一个相当大的程序。它已经运行了一段时间了,但是今天我尝试将其提高一个档次并增加问题的大小(这是研究非标准的有限元求解器,如果那样的话)。可以帮助任何人...)现在,
在 C 和 C++ 中,有许多操作会导致未定义的行为,即允许编译器做任何它想做的事情的情况。 Examples包括在释放变量后使用它,释放变量两次和取消引用空指针。 Fortran 是否也有未定义的行
通常我使用fortran进行数值分析,然后使用matlab、R和python进行后期和前期工作。 我发现 matlab、R 和 python 在终端中提供了命令提示符,以便您可以运行脚本以及从命令行立
在 Fortran 中将变量设置为 +Infinity 的最安全方法是什么?目前我正在使用: program test implicit none print *,infinity() con
我是一名优秀的程序员,十分优秀!