我在Fortran中创建一个程序,以弧度为单位输入x(sin),然后计算要计算的项数。
这是我的计划:
! Sine value using MacLaurin series
program SineApprox
implicit none
integer :: numTerms, denom, i
double precision :: x, temp, summ
! Read Angle in Radians, and numTerms
read(*,*) x, numTerms
! Computing MacLaurin series
denom = (2*numTerms)-1 !Gives denominator
temp = x !Temp calculates each term
summ = x
do i = 3, denom, 2
temp = (((-temp)*(x**2))/(i*(i-1)))
summ = summ + temp
end do
print *, summ
end program SineApprox
但是,我没有得到我教授输入所需的相同值:5 30
我的代码输出是:
-0.95892427466314001
但是,所需的输出是:
-0.95892427466313568
^^^^
我无法弄清楚错误的位置。
答案 0 :(得分:4)
我无法弄清楚错误的位置。
高精度sine(5.0)
为-0.95892427466313846889...
OP的结果在最佳答案的14 ULP范围内,而教授的结果是25 ULP。
因此OP没有问题。要获得与教授答案的完全匹配,您必须编写一个较差的方法。
教授劣质答案的一个简单可能原因是教授的代码仅在i<=30
(15个术语,而不是30个术语)时循环 - 这只能解释差异。尝试使用较少迭代的代码,看看哪个迭代计数最接近教授的答案。
// sine(5.0) Delta from correct Source
//
//-0.95892427466313846889... 0 000000 calc
//-0.95892427466313823 -23 889... chux (via some C code)
//-0.95892427466314001 +154 111... OP
//-0.95892427466313568 -278 889... Prof
// 0.00000000000000089 +/-89 ... ulp(5.0)
// 0.00000000000000011 +/-11 ... ulp(-0.9589)
// 12345678901234567(digit count)
注意:
接近x == 5.0
,大约在第17个学期之后,temp
项很小,不会对结果产生显着影响。所以肯定会使用足够的术语。
对于常见的FP表示,最后5位的单位是~89e-17。 ULP为-0.9589,如果是~11e-17。
答案 1 :(得分:3)
我将使用一些ArbitraryPrecisionFloat模拟这两个算法来说明因子解决方案在数字上有多糟糕:我将在这里使用Squeak Smalltalk,但语言并不重要,你可以用Maple或Python来做,只要你有一些任意的精确库...
与sin(5)
的确切结果最接近的binary64 floating point number为-0.9589242746631385
。
我们将看到两种算法在不同精度下接近理想值的程度(从单精度24位到长双精度64位)。
p := 24 to: 64.
e1 := p collect: [:n |
| x denom temp summ closest |
closest := (5 asArbitraryPrecisionFloatNumBits: 100) sin asFloat.
x := 5 asArbitraryPrecisionFloatNumBits: n.
numTerms := 30.
denom := (2*numTerms)-1.
temp := x.
summ := x.
3 to: denom by: 2 do: [:i |
temp := (((0-temp)*(x**2))/(i*(i-1))).
summ := summ + temp ].
(summ asFloat - closest) abs].
然后是因子重写:
e2 := p collect: [:n |
| x denom temp summ closest fact |
closest := (5 asArbitraryPrecisionFloatNumBits: 100) sin asFloat.
x := 5 asArbitraryPrecisionFloatNumBits: n.
numTerms := 30.
denom := (2*numTerms)-1.
temp := x.
summ := x.
3 to: denom by: 2 do: [:i |
fact := ((1 to: i) collect: [:k | k asArbitraryPrecisionFloatNumBits: n]) product.
temp := ((x ** i)*(-1 ** (i//2)))/fact.
summ := summ + temp ].
(summ asFloat - closest) abs].
然后我们可以用任何语言绘制结果(这里是Matlab)
p=24:64;
e1=[1.8854927952283163e-8 4.8657250339978475e-8 2.5848555629259806e-8 6.355841153382613e-8 3.953766758435506e-9 2.071757310151412e-8 2.0911216092045493e-9 6.941377472813315e-10 4.700154709880167e-10 9.269683909352011e-10 6.256184459374481e-11 3.1578795134379334e-10 2.4749646776456302e-11 3.202560439063973e-11 1.526812010155254e-11 8.378742144543594e-12 3.444688978504473e-12 6.105005390111273e-12 9.435785486289205e-13 7.617240171953199e-13 2.275957200481571e-14 1.6486811915683575e-13 2.275957200481571e-14 5.1181281435219717e-14 1.27675647831893e-14 1.2101430968414206e-14 1.2212453270876722e-15 2.7755575615628914e-15 5.551115123125783e-16 1.5543122344752192e-15 1.1102230246251565e-16 1.1102230246251565e-16 0.0 1.1102230246251565e-16 0.0 0.0 0.0 0.0 0.0 0.0 0.0];
e2=[9.725292443585332e-7 4.281799078631465e-7 2.721746682476933e-7 1.823107481646602e-7 9.336073392152144e-8 5.1925587718493205e-8 1.6992282803052206e-8 6.756442849642497e-9 5.1179199767048544e-9 3.0311525511805826e-9 1.2180066955025382e-9 6.155346232716852e-10 2.8668412088705963e-10 6.983780220792823e-11 6.476741365446514e-11 3.8914982347648674e-11 1.7473689162272876e-11 1.2084888645347291e-11 4.513389662008649e-12 1.7393864126802328e-12 1.273314786942592e-12 5.172529071728604e-13 2.5013324744804777e-13 1.6198153929281034e-13 6.894484982922222e-14 2.8754776337791554e-14 1.6542323066914832e-14 8.770761894538737e-15 4.773959005888173e-15 2.7755575615628914e-15 7.771561172376096e-16 3.3306690738754696e-16 3.3306690738754696e-16 1.1102230246251565e-16 1.1102230246251565e-16 0.0 0.0 0.0 0.0 0.0 0.0];
figure; semilogy(p,e1,p,e2,'-.'); legend('your algorithm','factorial algorithm'); xlabel('float precision'); ylabel('error')
您的算法表现更好:一个误差幅度小于因子变量:
因子变体中最糟糕的是它取决于内在幂函数x**power
。此函数不一定回答最接近精确结果的浮点,并且可能根据基础数学库实现而变化。因此,要求一个相同的结果,不仅取决于严格的IEEE 754合规性,而且还取决于实现定义的准确性是一个非常愚蠢的事情 - 除非所有学生都有完全相同的硬件和软件 - 但即便如此,它的教训是什么。每个科学家应该了解浮点数?
答案 2 :(得分:1)
我最后和一位得到确切答案的同学交谈。他告诉我他构建了一个阶乘函数,然后他建议我使用if else语句解决这些术语:if(odd),然后添加。否则if(偶数),然后减去。所以我遵循了他的建议并最终获得了正确的输出。
作为参考,这些是我教授的测试用例:
5 30 - &gt; -0.95892427466313568
4 100 - &gt; -0.75680249530792754
这是我的代码:
! Sine value using MacLaurin series
recursive function factorial(n) result (f)
double precision :: f
double precision, intent(in) :: n
if (n <= 0) then
f = 1
else
f = n * factorial(n-1)
end if
end function factorial
program SineApprox
implicit none
integer :: numTerms, i, oddeven
double precision :: x, summ, factorial, odd, even, power
! Read Angle in Radians, and numTerms
read(*,*) x, numTerms
! Computing MacLaurin series
summ = 0
power = 1
do i = 1, numTerms, 1
oddeven = modulo(i,2)
if(oddeven == 1) then
odd = (x**power)/factorial(power)
summ = summ + odd
else if (oddeven == 0) then
even = (x**(power))/factorial(power)
summ = summ - even
end if
power = power + 2
end do
print *, summ
end program SineApprox