如何计算`exp(x)/2`

问题描述 投票:0回答:1
试图写我的

sinh(x)

double my_sinh(double x)
,我遇到了角案。

x

在709.782 ...到710.475 ...范围,

如何计算
exp(x)/2

液体正弦

sinh(x)

和余弦
cosh(x)
exp(x)/2
(或the)时都很好地近似(或取决于
x >= 20
的类型详细信息)。 common
双重
,最大的有限值
x
约为1.798 ...x10

308

.。 有限的DBL_MAX

x的最积极的是大约710.475 .... 有限的最积极的
sinh(x)
大约为709.782 ....
由于溢出,以下
cosh(x)
可能为时已晚,无法实现有限的结果。
Codode可以使用较宽的精度/范围,例如
x
,但是
exp(x)
不确定比
exp(710.0)

/2

更好,而且我看上去只有一个仅解决方案。

long double
考虑到额外的宽
long double

double

	
如何计算exp(x)/2?

要避免溢出
double
但是e
x

/2在
#include <assert.h>
#include <math.h>
#include <stdio.h>

double my_sinh_f1(double x) {
  assert(x >= 709);  // Only concerned about large values for now.
  return (double) (expl(x)/2);  // For now, call a long double function.
}

/*
 * Over a range of values:
 * Compute the difference between f(x) and higher precision sinhl.
 * Scale difference to the unit-in-the-last-place (ULP) of the double result.
 * Return the worst case ULP.
 */
double test_sinh(double (f)(double), double x0, double x1, unsigned n) {
  double error_max = 0.0;
  double dx = (x1 - x0)/n;
  for (double x = x0; x <= x1; x += dx) {
    long double y0_as_ld = sinhl(x);
    double y0 = (double) y0_as_ld;
    double ulp = y0 > y0_as_ld ? y0 - nextafter(y0,0) : nextafter(y0,INFINITY) - y0;
    double y1 = f(x);
    double error = (double) ((y1 - y0_as_ld)/ulp);
    error = fabs(error);
    if (error > error_max) {
      error_max = error;
    }
  }
  return error_max;
}

int main(void) {
  double (*f)(double) = my_sinh_f1;
  double x0= log(DBL_MAX);
  double x1 = asinh(DBL_MAX);
  unsigned n = 1000000007; //1000003;  // Some prime
  printf("x0:%.3f, x1:%.3f n:%10u error:%g(ulp)\n", x0, x1, n, test_sinh(f, x0, x1, n));
}
范围内,请与精心选择的

sinhl()exp(x)

)一起使用以最大程度地生成错误。

使用数学身份:
c floating-point exp hyperbolic-function
1个回答
0
投票
e

x

/2 =e
(x -v)

*e

v
/2
我们可以选择常数v =log
e
(2),(或v == 0.693 ...)然后
e
x
/2 =e
(x- log(2))

*2/2

double

exp(x - v)*exp(v)/2

我们将取得成功的是消除v,但ULP错误跳至495。 这是因为将减法中的误差乘以0x1.62e4307c58800p-1函数的

magnitude。
Instead,让我们选择一个至少是log

e(2)的double my_sinh_f2(double x) { assert(x >= 709); static const double v = 0.69314718055994530941; // log(2) static const double scale = 2.0/2; return exp(x - v)*scale; }

,但ulp的倍数是

x0:709.783, x1:710.476 n:1000000007 error:495.895(ulp) long doublex

exp(x)
v

x

现在,由于减法为

Exact

,ULP误差已减少到1.792。 最终错误是next_power_of_2(709)/pow(2,53)(希望很小,例如

V
到a(约0.5)和乘法误差(另一个0.5)。
1024/9,007,199,254,740,992
我们可以做得更好吗?


如果扩展的精度值的扩展值是形式的

ulp = 1024.0/pow(2,53);
v = ceil(log(2)/ulp)*ulp; // 0.69314718056000401...
与。 EV1
是2.0000000000001174 ...或以十六进制的精度为
double my_sinh_f3(double x) { assert(x >= 709); // Note 10 LSbits are zero -----------vvv static const double v = 0x1.62e42fefa3c00p-1; // 0.69314718056000401...; // ceil(log(2)*pow(2,43))/pow(2,43) static const double scale = 2.0000000000001174152150690680826/2; // exp(v)/2 return exp(x - v)*scale; }
,但只有显着的(53个二进制数字)或
exp()
用作
double

常数,而剩余的
x0:709.783, x1:710.476 n:1000000007 error:1.79199(ulp)
 
是形成
exp(v2)
常数的误差。

我们可以选择一个偏移0x1.000000xxxxxxx000...p+1,以便其e

v
在其< 1.0) plus the conversion of ev1 = 0.69314718056000401...表示中的错误要小得多。 然后,用作一个
0x1.0000000000108654...p+1
的值将为

0x1.0000000000109p+1
,它比
double

的接近1000倍以上是

-0x0.0000000000000346...p+1
)/2。

对高于
scale
的值进行测试,并且是709的ULP的倍数(因此,缩写是准确的),一个值的值是:
v
导致:

double

double精度相比,它的零位为零:
0x1.0000000000xxxp+1
.
exp(v2)/2
so,对于此
0x1.0000000000109p+1
偏移,没有发生减法误差,并且使用的
exp(v1
比我们先前的尝试更接近e

v

Boo-yah!
log(2)
我们的1.00781(ULP)如何与库函数进行比较?  下面的报告我们几乎要好得多1.0 ULP。
v == 0.69314719694034466
图库的further测试
scale == 0x1.000000465a709000p+1/2
指示大约至
double
左右,其ULP非常好:0.6,然后在这个问题的范围内将ULP跳至1.920。 我怀疑我的海湾合作委员会图书馆
scale == 0x1.000000465a709p+1/2
会从类似的不断变化中受益。

注:

double my_sinh_f4(double x) {
  assert(x >= 709);
  // Note 10 LSbits are zero -----------vvv
  static const double v = 0x1.62e4307c58800p-1; // 0.69314719694034466;
  static const double scale = 0x1.000000465a709000p+1/2; // exp(v)/2
  return exp(x - v)*scale;
}

v
的类似调查导致:
scale
'scale = 0x1.005a780p+0f; sinhf()
x0:709.783, x1:710.476 n:1000000007 error:1.00781(ulp).
用作64位扩展精度的参考函数。 更深入的分析将使用

Proven
参考功能。
compiler注释:

sinh()

    

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.