数学函数的参数和返回值探秘
- 一、数学函数的参数
- 1.1 隐式类型转换
- 1.2 隐式类型转换的秘密
- 二、数学函数的返回值
本文所说的数学函数单指<math.h>中的系统函数,这些函数对参数和返回值的要求与其他类函数是有一点不同的。尤其是参数部分,是有值得深挖的东西的,本文将揭示这里面隐藏的秘密。
一、数学函数的参数
在C、C++中,数学函数的参数和返回值一般都要求是浮点型的,那么如果输入一个整数或者整数表达式作参数会怎么样呢?
1.1 隐式类型转换
以计算平方根的函数为例,共有3个函数,分别是sqrt, sqrtf, sqrtl,函数原型如下:
float sqrtf( float arg );
double sqrt( double arg );
long double sqrtl( long double arg );
按要求参数不能是整数,返回值也不能是整数。所以按理sqrt(9)是不符合函数要求的,而应写成sqrt(9.0),但实际上,用sqrt(9)也能输出正确结果。这是因为,在调用函数时,如果传入一个非接受类型的参数,会被隐式转换为接受的类型。
隐式类型转换又叫自动类型转换,常发生在赋值操作或表达式求值时。这种转换是由编译器自动进行的,不需要程序员显式地指定转换类型(这种叫强制类型转换或显式类型转换)。
为什么能自动进行转换呢?
因为sqrt(arg)函数被定义为接受double类型的参数,这一点对于编译器来讲是已知的,当传递int类型时,编译器自然会知道应该把它转换为什么类型,即会自动隐式转换为 double类型。
1.2 隐式类型转换的秘密
这里面有一个很有意思的事情,就是sqrt(arg)函数将参数arg由int转为double类型时,它实际上是将arg作为一个整体来转换的。比如sqrt(19/2)的计算过程:
1)求值。把19/2视为一个整体,先计算出它的值。因为分子分母都是整数,所以输出结果也是整型,计算结果会舍去小数部分,即19/2=9。
2)转换。将19/2的结果由int型转换为double,即将9转换为9.0,然后再计算sqrt(9.0)的值。
也就是说,它是先求值,后转换,所以sqrt(arg)相当于sqrt((double)(arg))。
(double)
就是前面说的强制类型转换,用带()的类型转换运算符来表示。
这一点其实也很好理解,函数的原型就是sqrt(arg),它的参数归根到底只有arg这位仁兄,至于arg是怎么计算来的,取决于它本身的表达式,和函数本身没有关系(即19/2的值只与19/2这个表达式本身有关,和sqrt函数无关)。sqrt(arg)只管把arg转化为浮点型,然后计算它的算术平方根。
注意sqrt((double)(arg))参数中的这两个括号都是非常有必要的,感兴趣的可以用下面的代码测试一下,就可以看出其中的玄机。
#include<stdio.h>
#include<math.h>
int main(){
printf("%f\n", sqrt(9.0));
printf("%f\n", sqrt(9));
printf("%f\n", sqrt(19/2));
printf("%f\n", sqrt((double)(19/2)));
printf("%f\n", sqrt((double)19/2));
printf("%f\n", sqrt(19/(double)2));
printf("%f\n", sqrt(19.0/2));
return 0;
}
程序输出结果:
看吧,前4个表达式是等价的,后3个表达式是等价的。
这里尤其注意以下两个表达式的不同:
printf("%f\n", sqrt((double)(19/2)));
printf("%f\n", sqrt((double)19/2));
前者的(double)
是可有可无的,就相当于printf("%f\n", sqrt(19/2));
,它们表达的都是同一个意思:先求值(19/2=9)、后转换。
后者由于19/2没有括号,命运彻底改变,(double)19/2
作为一个表达式,(double)和19才是最亲密的一对,因此先将19转换为浮点数,所以(double)19/2
的结果是9.5,然后再计算平方根。
一个是对9开平方,一个是对9.5开平方,结果自然不同。
注意:无论哪种写法,强制类型转换
(double)
中的括号是不能省略的,这是语法要求,即不能写成printf("%f\n", sqrt(double(19/2)));
,否则会报语法错误。
二、数学函数的返回值
sqrt的返回值也是double类型的,所以用printf输出时也不能用%d(如用了会输出0),而要用%f。
注意:
违反以上要求编译时也不会报错,但输出结果会有问题。