C 浮点中的相关幂

C 浮点中的相关幂,c,floating-point,ieee-754,numerical-analysis,C,Floating Point,Ieee 754,Numerical Analysis,给定运行时变量双值x和y,如果需要计算: double c1 = pow(x, y) double c2 = pow(x, y + 1.0) 从数值精度的角度来看,执行以下操作是否通常是安全的: double c1 = pow(x, y) double c2 = c1 * x; 您可以假设y>=-1.0。像这样的FP数学的每个步骤都会产生潜在的舍入错误 考虑y=powa,b如何近似y=a+误差a+误差b+误差y。 Error_y对Error_b非常敏感,尤其是当y较大时 当正y和1.0具有显

给定运行时变量双值x和y,如果需要计算:

double c1 = pow(x, y)
double c2 = pow(x, y + 1.0)
从数值精度的角度来看,执行以下操作是否通常是安全的:

double c1 = pow(x, y)
double c2 = c1 * x;

您可以假设y>=-1.0。

像这样的FP数学的每个步骤都会产生潜在的舍入错误

考虑y=powa,b如何近似y=a+误差a+误差b+误差y。 Error_y对Error_b非常敏感,尤其是当y较大时

当正y和1.0具有显著不同的量级时,加法可能会令人不安。 当它们相差很大时,双c2=功率x,y*x;这样更好

当y接近-1.0时,y+1.0抵消位,双c2=powx,y+1.0更好

只要y+1.0是精确的,就使用powx,y+1.0。 当y<0.0时,我会选择powx,y+1.0。 当y>1.0时,使用powx,y*x

一般来说,我会使用powx,y*x,因为最坏的情况可能只有几个s。一个好的pow有几个,另一个*有½个。在powa,b的b中注入错误在数值上是危险的

注:


考虑到最坏的情况是y很大而不是无限大。这实际上是powa,b中所有可能的a,b的一个小子集,因为结果通常是0.0或∞ 或NaN。

像这样的FP数学的每一步都会导致潜在的舍入错误

考虑y=powa,b如何近似y=a+误差a+误差b+误差y。 Error_y对Error_b非常敏感,尤其是当y较大时

当正y和1.0具有显著不同的量级时,加法可能会令人不安。 当它们相差很大时,双c2=功率x,y*x;这样更好

当y接近-1.0时,y+1.0抵消位,双c2=powx,y+1.0更好

只要y+1.0是精确的,就使用powx,y+1.0。 当y<0.0时,我会选择powx,y+1.0。 当y>1.0时,使用powx,y*x

一般来说,我会使用powx,y*x,因为最坏的情况可能只有几个s。一个好的pow有几个,另一个*有½个。在powa,b的b中注入错误在数值上是危险的

注:


考虑到最坏的情况是y很大而不是无限大。这实际上是powa,b中所有可能的a,b的一个小子集,因为结果通常是0.0或∞ 或NaN。

通常,使用这两个表达式可以得到不同的结果,因为pow使用对数来计算最终结果,而第二种情况在使用pow与乘积混合时使用对数的混合

从数学上讲,这两个表达式

pow(x, y+1.0) ~= exp((y+1.0)*log(x))    /* ~= here means approximately equal, not the C operator */

应该给出相同的近似结果,但由于求值的顺序和顺序不同,应该会出现不同的舍入误差

exp和log函数在其参数分别接近于0.0和1.0的情况下都有备选方案。因此,如果情况是x太接近1.0和/或y*logx太接近0.0,那么最好使用log1p和/或expm1变量

无论如何,对于你的实验,你可以尝试两个公式,并比较结果。即使您可以使用pow来实现,也可能会得到不同的结果——更准确,正如pow可能会在每次调用时决定的那样,哪个函数更适合使用

另一个提示是舍入误差与相乘的数字有关。。。因此,如果两个数字相乘前的相对误差为1.0E-10%,那么相对误差将保持不变,得到2.0E-10%,但当减去两个近似相等的量级时,相对误差将完全触发1.0000000000003-1.0==3.0E-13,但绝对误差保持不变,与加法之前一样,在1.0E-13上,这意味着对于3.0E-13的震级,现在的误差为1.0E-13,相对误差已提高到33.0%。如果您以后在产品中使用该数字,相对误差将转到产品,使您的整个计算毫无用处

基于这一原则,第二种方法似乎更好,因为它没有附加内容

还有第三种情况,如果只使用纯整数指数,可以应用fast

/* fea stands for (f)ast
 * (e)xponential (a)lgorithm (d)ouble */
double fead(double x, unsigned n)
{
    double result = 1.0;
    while (n) {
        if (n & 1)
            result *= x;
        x *= x; /* square x */
        n >>= 1; /* divide n by 2 */
    }
    return result;
}
下面是一些测试用例的完整实现:

#include <stdio.h>

/* fea stands for (f)ast
 * (e)xponential (a)lgorithm (d)ouble */
double fead(double x, unsigned n)
{
    double result = 1.0;
    while (n) {
        if (n & 1)
            result *= x;
        x *= x; /* square x */
        n >>= 1; /* divide n by 2 */
    }
    return result;
}

/* test cases to probe */
struct test_case {
   double base; /* base */
   unsigned exp; /* exponent */
   double expctd; /* expected result */
} test_cases_start [] = {
    { 10.0,     45, 1.0E45 },
    { 2.0,      63, 9.223372036854775808E18 },
    { 3.141593, 15, 2.865819336962266259E7 },
    { 0.1,      15, 1.0E-15 },
};

int main()
{
    struct test_case *test_cases_end
        = (struct test_case *)(&test_cases_start + 1);

    struct test_case *p;

    for (p = test_cases_start;
         p < test_cases_end;
         p++)
    {
        double calc = fead(p->base, p->exp);
        double diff = calc - p->expctd;
        printf("fead(%20.17g, %u)\n"
            " calculated = %20.17g\n"
            "   expected = %20.17g\n"
            " difference = %20.17g\n"
            " rel. error = %20.17g\n",
            p->base, p->exp,
            calc,
            p->expctd,
            diff,
            diff/p->expctd);
    }
}

通常,使用这两个表达式可以得到不同的结果,因为pow使用对数来计算最终结果,而第二种情况在使用pow混合乘积时使用对数的混合

从数学上讲,这两个表达式

pow(x, y+1.0) ~= exp((y+1.0)*log(x))    /* ~= here means approximately equal, not the C operator */

应该给出相同的近似结果,但由于求值的顺序和顺序不同,应该会出现不同的舍入误差

exp和log函数在其参数分别接近于0.0和1.0的情况下都有备选方案。因此,如果情况是x太接近1.0和/或y*logx太接近0.0,那么最好使用log1p和/或expm1变量

无论如何,对于你的实验,你可以尝试两个公式,并比较结果。即使你可以用pow来做,你可能会得到不同的结果——更准确,因为pow可能会 ecide,在每次调用时,哪个函数更适合使用

另一个提示是舍入误差与相乘的数字有关。。。因此,如果两个数字相乘前的相对误差为1.0E-10%,那么相对误差将保持不变,得到2.0E-10%,但当减去两个近似相等的量级时,相对误差将完全触发1.0000000000003-1.0==3.0E-13,但绝对误差保持不变,与加法之前一样,在1.0E-13上,这意味着对于3.0E-13的震级,现在的误差为1.0E-13,相对误差已提高到33.0%。如果您以后在产品中使用该数字,相对误差将转到产品,使您的整个计算毫无用处

基于这一原则,第二种方法似乎更好,因为它没有附加内容

还有第三种情况,如果只使用纯整数指数,可以应用fast

/* fea stands for (f)ast
 * (e)xponential (a)lgorithm (d)ouble */
double fead(double x, unsigned n)
{
    double result = 1.0;
    while (n) {
        if (n & 1)
            result *= x;
        x *= x; /* square x */
        n >>= 1; /* divide n by 2 */
    }
    return result;
}
下面是一些测试用例的完整实现:

#include <stdio.h>

/* fea stands for (f)ast
 * (e)xponential (a)lgorithm (d)ouble */
double fead(double x, unsigned n)
{
    double result = 1.0;
    while (n) {
        if (n & 1)
            result *= x;
        x *= x; /* square x */
        n >>= 1; /* divide n by 2 */
    }
    return result;
}

/* test cases to probe */
struct test_case {
   double base; /* base */
   unsigned exp; /* exponent */
   double expctd; /* expected result */
} test_cases_start [] = {
    { 10.0,     45, 1.0E45 },
    { 2.0,      63, 9.223372036854775808E18 },
    { 3.141593, 15, 2.865819336962266259E7 },
    { 0.1,      15, 1.0E-15 },
};

int main()
{
    struct test_case *test_cases_end
        = (struct test_case *)(&test_cases_start + 1);

    struct test_case *p;

    for (p = test_cases_start;
         p < test_cases_end;
         p++)
    {
        double calc = fead(p->base, p->exp);
        double diff = calc - p->expctd;
        printf("fead(%20.17g, %u)\n"
            " calculated = %20.17g\n"
            "   expected = %20.17g\n"
            " difference = %20.17g\n"
            " rel. error = %20.17g\n",
            p->base, p->exp,
            calc,
            p->expctd,
            diff,
            diff/p->expctd);
    }
}

你是说双c2=c1*x;而不是最后一行代码?@ChristopherMoore-是的,谢谢!固定。你是说双c2=c1*x;而不是最后一行代码?@ChristopherMoore-是的,谢谢!固定的