自己的ASIN()函数(泰勒)不准确 [英] Own asin() function (with Taylor series) not accurate

查看:217
本文介绍了自己的ASIN()函数(泰勒)不准确的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要写我自己的ASIN()函数,而与使用泰勒级数的文件math.h库。它的工作原理罚款℃之间数字&; -0.98; 0.98>但是当我接近极限,与1604迭代停止,因此是不准确的。

我不知道如何使它更accurete。任何建议都非常AP preciated!

在code为以下内容:

 的#include<&stdio.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&;&string.h中GT;#定义EPS 0.000000000001双my_arcsin(双X)
{
    长双一,一个B,BN;
    一个=一个= 1.0;
    B = BN = 2.0;
    长双N = 3.0;
    双XN;
    双XS = X;
    双XP = X;    INT iterace = 0;    XN = XS +(A / B)*(my_pow(XP中,n)/ N);    而(my_abs(XN - XS)> = EPS)
    {
        N + = 2.0;
        一个+ = 2.0;
        BN + = 2.0;
        A = A *的;
        B = B *亿元;        XS = XN;
        XN = XS +(A / B)*(my_pow(XP中,n)/ N);
        iterace ++;
    }    //的printf(%d个\\ N,iterace);    返回XN;
}INT主(INT ARGC,CHAR *的argv [])
{    双X = 0.0;    如果(argc个→2)
        X =的strtod(的argv [2],NULL);
    如果(的strcmp(的argv [1],--asin)== 0)
    {
           如果(X LT -1 || X - →1)
               的printf(南\\ n);
           其他
           {
               的printf(%10E \\ n,my_arcsin(X));
               //printf(\"%.10e\
,ASIN(X));
           }        返回0;
    }
}

也是我的价值和预期值的简短列表:

 我的价值观期望值my_asin(X)
5.2359877560e-01 5.2359877560e-01 0.5
1.5567132089e + 00 + 1.5707963268e 00 1 //问题
1.4292568534e + 00 + 1.4292568535e 0.99 00 //问题
1.1197695150e + 00 + 1.1197695150e 00 0.9
1.2532358975e + 00 + 1.2532358975e 00 0.95


解决方案

请注意:在这种情况下,我强烈建议@本斯的方法,因为你不能指望用低数据缓慢收敛的方法准确获得任意precision。

不过我愿意向你展示如何提高使用当前的算法的结果。

主要的问题是, A B 增长过快,很快成为 INF (后仅仅约150次迭代)。另一个类似的问题是 my_pow(XP,N)生长快,当 N 的增长,然而这并没有多大关系这种非常情况下,由于我们可以假设输入数据云的范围之内 [ - 1,1]。

所以,我刚刚通过引入改变了你对付 A / B 方法 ab_ratio ,看到我的编辑code:

 的#include<&stdio.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&;&string.h中GT;#定义EPS 0.000000000001#包括LT&;&math.h中GT;
#定义my_pow POWL
#定义my_abs fabsl双my_arcsin(双X)
{
    #如果0
    长双一,一个B,BN;
    一个=一个= 1.0;
    B = BN = 2.0;
    #万一
    无符号长_n = 0;
    长双ab_ratio = 0.5;
    长双N = 3.0;
    长双XN;
    长双XS = X;
    长双XP = X;    INT iterace = 0;    XN = XS + ab_ratio *(my_pow(XP,N)/ N);
    长双步= EPS;    #如果0
    而(my_abs(步骤)> = EPS)
    #其他
    而(1)/ *手动停止* /
    #万一
    {
        N + = 2.0;
        #如果0
        一个+ = 2.0;
        BN + = 2.0;
        A = A *的;
        B = B *亿元;
        #万一
        _n + = 1;
        ab_ratio * =(1.0 + 2.0 * _n)/(2.0 + 2.0 * _n);        XS = XN;
        步长= ab_ratio *(my_pow(XP,N)/ N);
        XN = XS +一步;
        iterace ++;
        如果(_n%千万== 0)
            的printf(%鲁%0.10克%G%G%G%G \\ N,_n,(双)XN(双)ab_ratio,(双)步,(双)XN(双)my_pow(XP,正));
    }    //的printf(%d个\\ N,iterace);    返回XN;
}INT主(INT ARGC,CHAR *的argv [])
{    双X = 0.0;    如果(argc个→2)
        X =的strtod(的argv [2],NULL);
    如果(的strcmp(的argv [1],--asin)== 0)
    {
           如果(X LT -1 || X - →1)
               的printf(南\\ n);
           其他
           {
               的printf(%10E \\ n,my_arcsin(X));
               //printf(\"%.10e\
,ASIN(X));
           }        返回0;
    }
}

有关 0.99 (甚至 0.9999999 ),它很快就给出了超过10显著位数正确的结果。然而,它越来越接近 1 时变得缓慢。结果
其实这个过程已经运行在我的笔记本计算近12分钟 - ASIN 1 ,而目前的结果是 1.570786871 35.6亿迭代。

更新:它已经现在1h51min,结果 1.570792915 和迭代计数 273.4亿

I need to write my own asin() function without math.h library with the use of Taylor series. It works fine for numbers between <-0.98;0.98> but when I am close to limits it stops with 1604 iterations and therefore is inaccurate.

I don't know how to make it more accurete. Any suggestions are very appreciated!

The code is following:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define EPS 0.000000000001

double my_arcsin(double x)
{
    long double a, an, b, bn;
    a = an = 1.0;
    b = bn = 2.0;
    long double n = 3.0;
    double xn;
    double xs = x;
    double xp = x;

    int iterace = 0;

    xn = xs + (a/b) * (my_pow(xp,n) / n);

    while (my_abs(xn - xs) >= EPS)
    {
        n += 2.0;
        an += 2.0;
        bn += 2.0;
        a = a * an;
        b = b * bn;

        xs = xn;
        xn = xs + (a/b) * (my_pow(xp,n) / n);
        iterace++;
    }

    //printf("%d\n", iterace);

    return xn;
}

int main(int argc, char* argv[])
{

    double x = 0.0;

    if (argc > 2)
        x = strtod(argv[2], NULL);
    if (strcmp(argv[1], "--asin") == 0)
    {
           if (x < -1 || x > 1)
               printf("nan\n");
           else
           {
               printf("%.10e\n", my_arcsin(x));
               //printf("%.10e\n", asin(x));
           }

        return 0;
    }
}

And also a short list of my values and expected ones:

My values              Expected values        my_asin(x)
5.2359877560e-01       5.2359877560e-01       0.5
1.5567132089e+00       1.5707963268e+00       1      //problem
1.4292568534e+00       1.4292568535e+00       0.99   //problem
1.1197695150e+00       1.1197695150e+00       0.9
1.2532358975e+00       1.2532358975e+00       0.95

解决方案

PLEASE NOTICE: In this case I strongly recommend @Bence's method, since you can't expect a slowly convergent method with low data accuracy to obtain arbitrary precision.

However I'm willing to show you how to improve the result using your current algorithm.

The main problem is that a and b grows too fast and soon become inf (after merely about 150 iterations). Another similar problem is my_pow(xp,n) grows fast when n grows, however this doesn't matter much in this very case since we could assume the input data goes inside the range of [-1, 1].

So I've just changed the method you deal with a/b by introducing ab_ratio, see my edited code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define EPS 0.000000000001

#include <math.h>
#define my_pow powl
#define my_abs fabsl

double my_arcsin(double x)
{
    #if 0
    long double a, an, b, bn;
    a = an = 1.0;
    b = bn = 2.0;
    #endif
    unsigned long _n = 0;
    long double ab_ratio = 0.5;
    long double n = 3.0;
    long double xn;
    long double xs = x;
    long double xp = x;

    int iterace = 0;

    xn = xs + ab_ratio * (my_pow(xp,n) / n);
    long double step = EPS;

    #if 0
    while (my_abs(step) >= EPS)
    #else
    while (1) /* manually stop it */
    #endif
    {
        n += 2.0;
        #if 0
        an += 2.0;
        bn += 2.0;
        a = a * an;
        b = b * bn;
        #endif
        _n += 1;
        ab_ratio *= (1.0 + 2.0 * _n) / (2.0 + 2.0 * _n);

        xs = xn;
        step = ab_ratio * (my_pow(xp,n) / n);
        xn = xs + step;
        iterace++;
        if (_n % 10000000 == 0)
            printf("%lu %.10g %g %g %g %g\n", _n, (double)xn, (double)ab_ratio, (double)step, (double)xn, (double)my_pow(xp, n));
    }

    //printf("%d\n", iterace);

    return xn;
}

int main(int argc, char* argv[])
{

    double x = 0.0;

    if (argc > 2)
        x = strtod(argv[2], NULL);
    if (strcmp(argv[1], "--asin") == 0)
    {
           if (x < -1 || x > 1)
               printf("nan\n");
           else
           {
               printf("%.10e\n", my_arcsin(x));
               //printf("%.10e\n", asin(x));
           }

        return 0;
    }
}

For 0.99 (and even 0.9999999) it soon gives correct results with more than 10 significant digits. However it gets slow when getting near to 1.
Actually the process has been running for nearly 12 minutes on my laptop calculating --asin 1, and the current result is 1.570786871 after 3560000000 iterations.

UPDATED: It's been 1h51min now and the result 1.570792915 and iteration count is 27340000000.

这篇关于自己的ASIN()函数(泰勒)不准确的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆