pow函数以及math.h的一些坑

您所在的位置:网站首页 pow的c语言 pow函数以及math.h的一些坑

pow函数以及math.h的一些坑

2024-03-24 15:06| 来源: 网络整理| 查看: 265

本文来自独孤伶俜的博客: pow函数以及math.h的一些坑 - 独孤伶俜

pow函数以及math.h的一些坑 起源

任何问题都有起源不是?这道问题其实是我刚学C语言的时候就自己遇到过。加上最近好多人问我类似于这种问题,于是决定写篇blog来解释一下。。。

问题描述

主要是C语言函数库中main.h中的pow(); 等函数有这个问题 问题的重现性和随机性还有得到的结果可能和你的编译器和cpu架构有关 下面是一个栗子:

#include #include int main() { int i = 2; printf("%d\n",(int)pow(10,2)); printf("%d\n",(int)pow(10,i)); return 0; }

将上面代码在 windows10,linux,Android下运行,得到结果如下: 三系统的结果

咦 为啥windowds的结果会不一样呢 先看看pow()函数的原型定义 double pow (double base, double exponent);

可以看到这个函数定义的 参数 和 返回值 都是 double 类型

这就意味着我们用int类型参与运算是需要强制转换~

接下来让我们来分析一下下~ 首先 我们得知道三大平台所对应的编译器是什么(win的cfree5.0用的的clang编译器,Android的C4roid 和 我的Deepin系统用的都是gcc编译器)其实 gcc和clang是两种完全不同的编译器,实现方式,编译步骤甚至都不一样,他们各有各的优势和劣势~,这里就不解释了~~(其实是解释不了!!!)~~最后 我们研究一下子这两编译器生成的中间代码 原来是编译器的锅!!!!!

我们看下中间代码~~(篇幅有限就不贴了)~~看到 :

Android和deepin的gcc编译器生成的代码把int变量的i值在调用pow函数前强制转换为double类型参与运算于是

int i = 2; printf("%d\n",(int)pow(10,i)); //就变成了 printf("%d\n",(int)pow(10,(double)i)); //最终就成了 printf("%d\n",(int)pow(10,2.0));

而反观Windows下的Clang编译器 他没有把int的i变成double值,于是……

int i = 2; printf("%d\n",(int)pow(10,i)); //编译器不知道i是个什么玩意,就会按double的形式把i的二进制数据取出来参与运算,,, //所以i的值就不知道是啥了,,,运气好的话值可能不会差太远 //如果运气不好,,,那完蛋了,失之毫厘谬以千里也

而常数2为啥不会出错呢

printf("%d\n",(int)pow(10,2)); //这是因为两种编译器都会自动转换常数的类型 //就相当于 printf("%d\n",(int)pow(10.0,2.0)); //所以两个常数都是以double类型参与运算的 //而在遇到 %d 时 他会把之前准备好的(int)强制转换的值给printf 所以不会出错咯~ 解决办法

说了这么半天,那到底咋解决呢!!!貌似有好几种方法 哈哈哈

方法一 //涉及整数的幂运算、阶乘等等你就不要使用pow(),自己写个函数随便起个名 不要用double就行了 方法二 //全程使用double运算 最后强制转换为int类型再输出什么的 //比如刚刚上面的代码可以写成这样 double i = 2.0; int aut1 = (int)pow(10.0,i); int aut2 = (int)pow(10.0,2.0); printf("%d\n",aut1); printf("%d\n",aut2; 方法三 //早日跳坑吧 //程序员没有女友 //有很大风险秃头 //钢铁直男 //But //I love programming //I love programming BUG //I love you //咳咳,我也爱读沈从文的《边城》 总结

其实不止pow函数,让我们看看math.h中的其他的函数原型

//三角函数 double sin(double);//正弦 double cos(double);//余弦 double tan(double);//正切 //反三角函数 double asin (double); //结果介于[-PI/2,PI/2] double acos (double); //结果介于[0,PI] double atan (double); //反正切(主值),结果介于[-PI/2,PI/2] double atan2 (double,double); //反正切(整圆值),结果介于[-PI,PI] //绝对值 int abs(int i); //求整型的绝对值 double fabs (double); //求实型的绝对值 double cabs(struct complex znum); //求复数的绝对值

上面这些函数大部分是用的double作为参数和返回值的,所以都有可能出现今天我们谈论的莫名其妙的bug,哈哈哈

尽量避免发生类型强制转换的情况发生,即使无法避免,也要写清楚(类型)强制转换,后面维护也会轻松不少~~~


【本文地址】


今日新闻


推荐新闻


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