C 浮点中的相关幂
给定运行时变量双值x和y,如果需要计算: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具有显
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-是的,谢谢!固定的