Fortran Coder

标题: fortran 程序运行不守恒是什么问题?急 [打印本页]

作者: 八爪鱼    时间: 2023-8-22 10:20
标题: fortran 程序运行不守恒是什么问题?急
大家好,在这里发一个求助帖。
问题是这样的:在数学上可以验证出来三个函数表达式相加(三个函数表达式相加的结果与任何数都无关,之和就是守恒的)是绝对守恒的,但是用Fortran程序运行出来的结果不守恒,请问一下大家可能是什么问题?

作者: kyra    时间: 2023-8-22 11:53
函数数值不稳定,浮点数误差等等。
作者: 八爪鱼    时间: 2023-8-23 10:44
那请问这个问题要怎么解决啊,怎么才能提高精度啊?我在写程序的时候都已经用的是双精度数了,求解,
作者: 八爪鱼    时间: 2023-8-23 10:45
kyra 发表于 2023-8-22 11:53
函数数值不稳定,浮点数误差等等。

或者说,根据经验,哪个编译器的运算精度更高?
作者: kyra    时间: 2023-8-23 14:50
编译器的差别几乎可以忽略,主要还是要针对算法本身做优化,这方面的经验不容易总结和归纳。
如果你的算法不是很复杂,可以给出来,大家有针对性的探讨。
作者: 八爪鱼    时间: 2023-8-24 09:27
kyra 发表于 2023-8-23 14:50
编译器的差别几乎可以忽略,主要还是要针对算法本身做优化,这方面的经验不容易总结和归纳。
如果你的算法 ...

其实程序是很简单的,只有几个解析式,就是最基本的加减乘除和乘方运算,连微积分都不涉及。但是它运行结果就是不守恒了。
而且我用Excel表格计算,用其他的电脑试,包括用其他的程序语言进行实验,出现的结果相同,都是不守恒的。
作者: 八爪鱼    时间: 2023-8-24 09:42
八爪鱼 发表于 2023-8-23 10:45
或者说,根据经验,哪个编译器的运算精度更高?

[Fortran] 纯文本查看 复制代码
program sasm          !model with P32
    implicit real*8(a-h,o-z)
    integer,parameter::n=15
    integer,parameter::i=10
    real*8,parameter::a=1d0
    real*8,dimension(n)::p12,p21,p23,p32
    real*8,dimension(n)::b,c
    real*8,dimension(n)::r1,r2
    real*8,dimension(n)::c1,c2,c3
    real*8,dimension(n)::m1,m2,m3
    real*8,dimension(n)::x1,x2,x3
    real*8,dimension(n)::yn1,yn2,yn3,yn
   
    open(1,file='D:/program/0809/frequency12.txt')
    open(2,file='D:/program/0809/frequency21.txt')
    open(3,file='D:/program/0809/frequency23.txt')
    open(5,file='D:/program/0809/frequency32.txt')
    open(7,file='D:/program/0809/1200.txt')
   
    do ix=1,n
        read(1,*)xtemp,ytemp,p12(ix)
        read(2,*)xtemp,ytemp,p21(ix)
        read(3,*)xtemp,ytemp,p23(ix)
        read(5,*)xtemp,ytemp,p32(ix)
    end do
    close(1)
    close(2)
    close(3)
    close(5)
   
    b(i)=p12(i)+p21(i)+p23(i)+p32(i)
    c(i)=p12(i)*p23(i)+p12(i)*p32(i)+p21(i)*p32(i)
   
    r1(i)=(-1d0*b(i)+dsqrt(b(i)**(2d0)-4d0*a*c(i)))/(2d0*a)
    r2(i)=(-1d0*b(i)-dsqrt(b(i)**(2d0)-4d0*a*c(i)))/(2d0*a)

    c1(i)=p12(i)*(r2(i)+p12(i)+p21(i))/(r1(i)*(r1(i)-r2(i)))
    c2(i)=p12(i)*(r1(i)+p12(i)+p21(i))/(r2(i)*(r2(i)-r1(i)))
    c3(i)=1d0-c1(i)-c2(i)
   
    m1(i)=(r1(i)+p12(i))/p21(i)
    m2(i)=(r2(i)+p12(i))/p21(i)
    m3(i)=p12(i)/p21(i)
   
    x1(i)=(r1(i)**(2d0)+r1(i)*(p12(i)+p21(i)+p23(i))+p12(i)*p23(i))/(p21(i)*p32(i))
    x2(i)=(r2(i)**(2d0)+r2(i)*(p12(i)+p21(i)+p23(i))+p12(i)*p23(i))/(p21(i)*p32(i))
    x3(i)=(p12(i)*p23(i))/(p21(i)*p32(i))
   
   
    do t=0d0,10000000d0,10000d0
        yn1(i)=c1(i)*dexp(r1(i)*t)+c2(i)*dexp(r2(i)*t)+c3(i)
        yn2(i)=c1(i)*m1(i)*dexp(r1(i)*t)+c2(i)*m2(i)*dexp(r2(i)*t)+c3(i)*m3(i)
        yn3(i)=c1(i)*x1(i)*dexp(r1(i)*t)+c2(i)*x2(i)*dexp(r2(i)*t)+c3(i)*x3(i)
        
        yn(i)=yn1(i)+yn2(i)+yn3(i)
        
        write(*,*)t,yn(i)
        write(7,*)yn1(i),yn2(i),yn3(i),yn(i)   
      end do
    stop
    end program sasm

作者: 八爪鱼    时间: 2023-8-24 09:44
kyra 发表于 2023-8-23 14:50
编译器的差别几乎可以忽略,主要还是要针对算法本身做优化,这方面的经验不容易总结和归纳。
如果你的算法 ...

您好,我已经把我的代码贴出来了,里面可能有误差的有一个e指数的运算,能方便帮我看一下吗?程序整体上来说是比较简单的
作者: 风平老涡    时间: 2023-8-24 22:20
本帖最后由 风平老涡 于 2023-8-24 22:31 编辑
八爪鱼 发表于 2023-8-24 09:44
您好,我已经把我的代码贴出来了,里面可能有误差的有一个e指数的运算,能方便帮我看一下吗?程序整体上 ...

有几个问题:
1)循环变量建议用整数型
2)从31行到47行没有在循环内,所有的计算都没有意义。
3)从51行到58行数组引用与循环变量名不符,

作者: 八爪鱼    时间: 2023-8-25 09:25
风平老涡 发表于 2023-8-24 22:20
有几个问题:
1)循环变量建议用整数型
2)从31行到47行没有在循环内,所有的计算都没有意义。

您好,非常感谢您提的建议。
我在这里给您回复一下:
1、关于您提到的循环变量用整数型,循环变量t是要参与计算的,其他的都是浮点型,如果只有t是整型量的话,不知道对计算结果是否有影响,这个我再试一下;
2、31行到47行没有在循环内,这个是这样的,主要是31行到47行中有变量i,而变量i我在第4行已经定义了。这个程序需要提前读取文件,文件里面有15个不同的数。所以定义了一个变量i,让其从1到15,每次在给定i的情况下,再让程序进行下面的循环计算,i的值我是每次手动改的;
3、数组引用与循环变量名不符?可以再解释一下吗?我这边没懂您说的是什么意思。
作者: 风平老涡    时间: 2023-8-25 21:10
八爪鱼 发表于 2023-8-25 09:25
您好,非常感谢您提的建议。
我在这里给您回复一下:
1、关于您提到的循环变量用整数型,循环变量t是要参 ...

1)循环变量用整数型,不会对计算结果有影响
2)了解你的思路,以上2)和3)就没有问题了。除了p12,p21,p23,p32是数组,其他数组可以为简单变量,不过对结果没有影响。
3)请显示4个输录文件的前几行的数据。
4)请显示p12,p21,p23,p32前几行的数据。
5)请显示yn1(i),yn2(i),yn3(i)和yn(i)前几行的数据。
作者: 八爪鱼    时间: 2023-8-27 10:18
风平老涡 发表于 2023-8-25 21:10
1)循环变量用整数型,不会对计算结果有影响
2)了解你的思路,以上2)和3)就没有问题了。除了p12,p21 ...

对的,对的,其实如果不想定义数组的话,p12,p21,p23,p32直接定义为常量也是可以的。
您好,非常感谢。我这边可以把p12,p21,p23,p32的问价给到您。
请允许我再重新描述一下我这边遇到的问题:在数学上已经证明yn1+yn2+yn3=1(yn)是恒量,与任何数(p12,p21,p23,p32,还有程序中的t)都无关。按道理来讲,不管p12,p21,p23,p32取任何数,程序运行出来的yn=1才对,但是实际情况却不是这样的,我这边把p32减小,立马就不守恒了
作者: 风平老涡    时间: 2023-8-28 10:16
标题: 注意
本帖最后由 风平老涡 于 2023-8-28 10:24 编辑
八爪鱼 发表于 2023-8-27 10:18
对的,对的,其实如果不想定义数组的话,p12,p21,p23,p32直接定义为常量也是可以的。
您好,非常感谢。我 ...

看上去入r1和r2是二元方程的两个根。如果两个根很接近的话,根据程序中的算法, c1, c2和c3会产生较大误差,引起最后结果不符预期。
请注意,两个相近的数相减会使有效位数减小从而引起误差。所以在编程中一定要注意两个操作:相近的数相减及除以一个很小的数。
作者: 八爪鱼    时间: 2023-8-28 13:26
风平老涡 发表于 2023-8-28 10:16
看上去入r1和r2是二元方程的两个根。如果两个根很接近的话,根据程序中的算法, c1, c2和c3会产生较大误差 ...

对的,r1和r2是一元二次方程的两个根
作者: 八爪鱼    时间: 2023-8-28 13:41
风平老涡 发表于 2023-8-28 10:16
看上去入r1和r2是二元方程的两个根。如果两个根很接近的话,根据程序中的算法, c1, c2和c3会产生较大误差 ...

您好,哥。我刚才用Excel表格分步算了一下。当p32=0.1时,r1和r2相差了12个数量级。我在想是不是在后续的计算时,由于r1,r2相差太大造成的误差,计算机把较小的数忽略了。
作者: 风平老涡    时间: 2023-8-28 23:02
本帖最后由 风平老涡 于 2023-8-29 02:11 编辑
八爪鱼 发表于 2023-8-28 13:41
您好,哥。我刚才用Excel表格分步算了一下。当p32=0.1时,r1和r2相差了12个数量级。我在想是不是在后续的 ...

用你的数据算了一下,当p32=0.1时发现几个问题:
1)当t=0时, yn1和 yn2 相对精确, yn3 误差较大。这是典型的误差传递,因为 yn3运算步骤较多。请参考有关数值分析书开头几章关于误差传递的描述。
2)当t>0时, 如t=1, 计算指数函数有溢出,无法得到准确解。
建议改变算法,算法需考虑误差传递及函数的变量可用范围。
作者: 八爪鱼    时间: 2023-8-29 09:40
风平老涡 发表于 2023-8-28 23:02
用你的数据算了一下,当p32=0.1时发现几个问题:
1)当t=0时, yn1和 yn2 相对精确, yn3 误差较大。这是 ...

非常感谢,我这边再试一下。关于数值分析的书,有哪些好的可以推荐一下吗?
如果改变算法,考虑范围的话,不守恒的问题是不是大概率就可以解决了。
作者: 风平老涡    时间: 2023-8-29 10:17
八爪鱼 发表于 2023-8-29 09:40
非常感谢,我这边再试一下。关于数值分析的书,有哪些好的可以推荐一下吗?
如果改变算法,考虑范围的话 ...

数值分析的书有很多,关於误差传递的基础知识所有的书都会涉及。
作者: 八爪鱼    时间: 2023-8-30 09:31
风平老涡 发表于 2023-8-29 10:17
数值分析的书有很多,关於误差传递的基础知识所有的书都会涉及。

好的,非常感谢提醒,我去看一下。谢谢
作者: 八爪鱼    时间: 2023-8-30 09:51
风平老涡 发表于 2023-8-28 23:02
用你的数据算了一下,当p32=0.1时发现几个问题:
1)当t=0时, yn1和 yn2 相对精确, yn3 误差较大。这是 ...

您好,哥,关于函数变量的可用范围,能再稍微提示一下吗?或者说Fortran程序书上是不是有关于溢出这一块的介绍?我自己看一下。刚刚接触Fortran和数值计算这一块,还是小白,有点不太懂。非常感谢!
作者: 风平老涡    时间: 2023-8-30 22:00
八爪鱼 发表于 2023-8-30 09:51
您好,哥,关于函数变量的可用范围,能再稍微提示一下吗?或者说Fortran程序书上是不是有关于溢出这一块 ...

可以百度一下,双精度数值范围在正负 2.2x10^-308 到 1.7x10^308,有效位数15~17。对于指数函数e^x的直接计算,x值的范围在正负~710。所以对x值较大的计算,必须对原始数学公式进行变换,比如取对数或提取公约数等。
作者: 八爪鱼    时间: 2023-8-31 09:27
风平老涡 发表于 2023-8-30 22:00
可以百度一下,双精度数值范围在正负 2.2x10^-308 到 1.7x10^308,有效位数15~17。对于指数函数e^x的直接 ...

好的,我这边试一下,非常感谢




欢迎光临 Fortran Coder (http://bbs.fcode.cn/) Powered by Discuz! X3.2