c语言实现pow(x,y)函数

您所在的位置:网站首页 c语言里的pow函数 c语言实现pow(x,y)函数

c语言实现pow(x,y)函数

2023-08-14 12:08| 来源: 网络整理| 查看: 265

0.math库里的实现(库里?)

在写这个函数之前,我想先查查c语言math库里自带的pow是怎么实现的 我先是在math.h里找,结果只找到了这个

_CRTIMP double __cdecl pow (double, double);

和这个

/* 7.12.7.4 The pow functions. Double in C89 */ extern float __cdecl powf (float, float); #ifndef __NO_INLINE__ __CRT_INLINE float __cdecl powf (float x, float y) {return (float) pow (x, y);} #endif extern long double __cdecl powl (long double, long double);

您这只给个声明不给我函数定义是几个意思啊 既然库文件查不到,我就上谷歌里搜搜吧 突然,不知怎的,我有一种不好的预感 果然,我啥也没查到,倒是有一大堆人在那里研究pow是怎么实现的 更有甚者,只写了一个for循环,然后给出示例

double value3 = mypow(5.21,4.11); printf("value3 = %f\n", value3);

在这里插入图片描述 我还特意用手机算了一下5.21的4.11次幂 在这里插入图片描述 您说您这不是自欺欺人吗? 但我就好奇了,math.h的源代码到底定义在哪里呢? 终于在知乎上找到了答案,感谢Belleve大佬,点击这里查看

math.h 里的函数都是定义在 libm 里,而每个 libm 实现都不同 gcc 的 glibm 中数学函数的实现完全是平台依存的,在 x86 机器上,能调用 FPU 指令的就用 FPU(比如 sqrt() 就实际上调用 FSQRT,log() 调用的是 FYL2X),否则再自己实现 如果需要软件实现,方法基本上是泰勒级数。当然对于 sin 这类,可以用专门的优化算法,比如 CORDIC CPU 中的电路基本上也就是泰勒级数。

原来是个叫libm的东西,gcc的就叫glibm 那glibm在哪里呢?在glibc,也就是c标准库里 是在是不想再在mac里找各种文件了,我再重新下载一个吧 libm.so位于 libc6-dev 这个包里,那就到pkgs.org上找找 结果网速感人,5MB的文件可能要下十分钟,在这个空档,我又搜了一下x86浮点运算指令集 果然不好好学汇编是要付出代价的,这些用软件实现费劲的函数,几条汇编指令就搞定了,比如说以下几条

指令 说明 f2xm1 计算2的乘方(次数为st0中的值),减去1 fyl2x 计算st1*log st0 以2为底 fyl2xp1 计算st1*log (st0 + 1) 以2为底

我又想到存储浮点数的IEEE标准,这个计算乘方和log以2为底真是引人深思啊 既然浮点数在寄存器里就是指数形式储存的,直接在[1:8]位操作不就行了?再加上上面的浮点运算指令,搞定! 这时候文件也下载好了,开始念咒语(Windows的同学别学我哈,容易被室友当成疯子)

ar -vx libc6-dev_2.28-2_i386.deb tar -xzvf data.tar.gz

然后肉眼搜索(一个一个文件夹点开……) 终于在usr/lib/i386-linux-gnu里找到了libm.so 干啊,.so的文件我还打不开,.so惹不起.a总能打开吧 敲代码!

#include int main() { FILE *fp = fopen("libm.a","rb"); char element; while(!feof(fp)){ element = getc(fp); printf("%2x",element); } fclose(fp); return 0; }

然而也仅仅是打开了而已,有啥用呢,读起来要累死人 还是用synalysis吧,Synalyze It! 结果对着一大堆二进制文件synalyze半个小时,体验极其糟糕 看到了一大堆ieee754,和各种powf32,powf64 因为最后实在读不下去了,就姑且认为我的猜测就是正确的吧(我咋怎么懒) 要是有哪位大佬看到了pow在libm.a里的实现,请务必告诉我!!! 其实我还想知道像f2xm1,fyl2x这样的指令在底层(门电路层次)是如何实现的,计算系统基础扎实的同学请务必带带我!!!

不扯淡了,我们现在切入正题,尝试用不同的方法实现pow函数

1.牛顿迭代法实现pow

其实这个方法是很容易想到的,特别是在尝试用牛顿迭代法实现sqrt函数之后,很自然的联想到也可以用这个方法解决pow函数的实现 首先考虑计算pow(2.17,3.14) 我们可以用for循环实现pow(2.17,3) 接下来只要把pow(2.17,0.14)也计算出来,再把两部分乘到一起,问题就解决了 后面的那部分,也就是一个数的小数次幂怎么算呢? 我再分 将0.14分成0.1和0.04,0.04就是0.01*4 由牛顿迭代法,我们就可以求出pow(2.17,0.1)和pow(2.17,0.01),以及整数次幂pow(2.17,3),这样我们就可以把这个值算出来 还是稍微写一下原理吧 计算2.170.1,即解出方程x10=2.17的根,然后百度百科吧……

用牛顿迭代法解非线性方程,是把非线性方程 f(x)=0线性化的一种近似方法。把 f(x)在点x0的某邻域内展开成泰勒级数 取其线性部分(即泰勒展开的前两项),并令其等于0,即 f(x0)+f’(x0)(x-x0)=0,以此作为非线性方程f(x)=0的近似方程,若f’(x0)!=0,则其解为 在这里插入图片描述 这样,得到牛顿迭代法的一个迭代关系式 在这里插入图片描述

下面还有一些细节问题,例如当pow(x,y)中的x为负数,y为整数时,函数会正常工作,但y若是小数,就会返回nan 那我们根据x和y的取值列一个表格

x,y情况 返回值 x>0&&y>0 计算,存储为answer,return answer x>0&&y=0 return 1 x>0&&y0 return 0 x=0&&y


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3