C++学习------cmath头文件的源码学习06

241 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第28天,点击查看活动详情

续接上文:C++学习------cmath头文件的源码学习05

函数族定义---双曲函数

  • cosh---计算双曲余弦函数
  • sinh---计算双曲正弦函数
  • tanh---计算双曲正切函数
  • acosh---计算双曲余弦面积
  • asinh---计算双曲正弦面积
  • atanh---计算双曲正切面积 同样的,我们来分析看下sinh的计算过程 glibc/sysdeps/ieee754/dbl-64/e_sinh.c
 44 double
 45 __ieee754_sinh (double x)
 46 {
 47   double t, w, h;
 48   int32_t ix, jx;
 49   uint32_t lx;
 50 
 51   /* High word of |x|. */
 52   GET_HIGH_WORD (jx, x);
 53   ix = jx & 0x7fffffff;
 54 
 55   /* x is INF or NaN */
 56   if (__glibc_unlikely (ix >= 0x7ff00000))
 57     return x + x;
 58 
 59   h = 0.5;
 60   if (jx < 0)
 61     h = -h;
 62   /* |x| in [0,22], return sign(x)*0.5*(E+E/(E+1))) */
 63   if (ix < 0x40360000)                  /* |x|<22 */
 64     {
 65       if (__glibc_unlikely (ix < 0x3e300000)) {            /* |x|<2**-28 */
 66     math_check_force_underflow (x);
 67     if (shuge + x > one)
 68       return x;
 69     /* sinh(tiny) = tiny with inexact */
 70       }
 71       t = __expm1 (fabs (x));
 72       if (ix < 0x3ff00000)
 73     return h * (2.0 * t - t * t / (t + one));
 74       return h * (t + t / (t + one));
 75     }
 76 
 77   /* |x| in [22, log(maxdouble)] return 0.5*exp(|x|) */
 78   if (ix < 0x40862e42)
 79     return h * __ieee754_exp (fabs (x));
 80 
 81   /* |x| in [log(maxdouble), overflowthresold] */
 82   GET_LOW_WORD (lx, x);
 83   if (ix < 0x408633ce || ((ix == 0x408633ce) && (lx <= (uint32_t) 0x8fb9f87d)))
 84     {
 85       w = __ieee754_exp (0.5 * fabs (x));
 86       t = h * w;
 87       return t * w;
 88     }
 89   
 90   /* |x| > overflowthresold, sinh(x) overflow */
 91   return math_narrow_eval (x * shuge);
 92 }

通过对源码的阅读,我们也能比较清晰的了解它的计算原理:

  1. 获取输入double的高位字jx,并且截断符号位得到ix;
  2. 判断无穷大(INF)和Nan的情况,直接返回x+x;
  3. 对ix进行划分:
  • ix < 0x40360000:|x| in [0,22],使用sign(x)0.5(E+E/(E+1)))进行计算,同时注意一些边界条件;
  • ix < 0x40862e42:|x| in [22, log(maxdouble)],使用0.5*exp(|x|)进行计算;
  • ix < 0x408633ce || ((ix == 0x408633ce) && (lx <= (uint32_t) 0x8fb9f87d)):|x| in [log(maxdouble), overflowthresold],使用hwwexp(0.5|x|)进行计算;
  • 其它情况,出现越界了,|x| > overflowthresold, sinh(x) overflow。

函数族定义---指数和对数函数

exp---返回自然对数e的x次方

double exp (double x);

frexp---获取有效数字和指数

double frexp (double x     , int* exp);

其中x为输入数,exp为存储指数的地址,计算如下的公式 x=significand2exponentx = significand * 2^{exponent}其中,significand在[0.5,1) 之间 如:

  param = 8.0;
  result = frexp (param , &n);
  printf ("%f = %f * 2^%d\n", param, result, n);
  //输出结果:
//8.000000 = 0.500000 * 2^4

ldexp---生成数字和指数有效数字

double ldexp (double x     , int exp);

返回x 2expx * 2^{exp} 如:

  param = 0.95;
  n = 4;
  result = ldexp (param , n);
  printf ("%f * 2^%d = %f\n", param, n, result);
  输出结果:
  0.950000 * 2^4 = 15.200000

log---返回自然对数

double log (double x);

log10---返回以10为底的对数

double log10 (double x);

如:

  param = 1000.0;
  result = log10 (param);
  printf ("log10(%f) = %f\n", param, result );
  //返回结果
log10(1000.000000) = 3.000000

modf---将数字拆分为整数和分数两个部分

double modf (double x     , double* intpart);

x为输入数字,intpart保存整数部分,小数部分返回,同时,整数部分和小数部分的符号相同

exp2---计算2x2^x

double exp2 (double x);

expm1---计算ex1e^x-1

 double expm1 (double x);

ilogb---返回以2为底对数的整数部分

int ilogb (double x);

先对x做以2为底的对数,然后取出其中的整数部分返回 如:

  param = 10.0;
  result = ilogb (param);
  printf ("ilogb(%f) = %d\n", param, result);
  //输出结果
  ilogb(10.000000) = 3

因为8<10<16,所以log2(8)<log2(10)<log2(16)log_2(8)<log_2(10)<log_2(16)即,3<log2(10)<43<log_2(10)<4,取其中的整数部分就是3。

log1p---返回ln(1+x)

double log1p (double x);

log2---返回log2(x)log_2(x)

double log2 (double x);

如:

  param = 1024.0;
  result = log2 (param);
  printf ("log2 (%f) = %f.\n", param, result );
  //输出结果
log2 (1024.000000) = 10.000000

logb---返回以FLT_RADIX为底的对数,在大多数平台上,这个值为2

double logb (double x);

基本同log2

scalbn---计算xFLT_RADIXnx * {FLT\_RADIX}^n

double scalbn (double x     , int n);

如:

  param = 1.50;
  n = 4;
  result = scalbn (param , n);
  printf ("%f * %d^%d = %f\n", param, FLT_RADIX, n, result);
  //输出结果:
1.500000 * 2^4 = 24.000000

scalbln---同scalbn,第二个入参可以是long

double scalbln (double x     , long int n);

后续函数分析见下一节