本文转自:http://zhedahht.blog.163.com/blog/static/254111742009101563242535/
题目:实现函数double Power(double base, int exponent),求base的exponent次方。不需要考虑溢出。
分析:这是一道看起来很简单的问题。可能有不少的人在看到题目后30秒写出如下的代码:
double Power(double base, int exponent)
{
double result = 1.0;
for(int i = 1; i <= exponent; ++i)
result *= base;
return result;
}
上述代码至少有一个问题:由于输入的exponent是个int型的数值,因此可能为正数,也可能是负数。上述代码只考虑了exponent为正数的情况。
我们知道当指数为负的时候,可以先对指数求绝对值,然后算出次方的结果之后再取倒数。既然有求倒数,我们很自然就要想到有没有可能对0求倒数,如果对0求倒数怎么办?当底数是零且指数是负数的时候,如果不做特殊处理,就会出现对0求倒数从而导致程序运行出错。对于函数出错通常有3种标识函数出错的方法:返回值、全局代码和异常。面试的时候可以向面试官阐述每种方法的优缺点,然后一起讨论决定选用哪种方式。
最后需要指出的是,由于0的0次方在数学上市没有意义的,因此无论是输出0还是1都是可以接受的,但这都需要和面试官说清楚,表明我们已经考虑到这个边界值了。还有一个细节就是:在判断底数base是不是等于0时,不能直接写0 == base, 这时因为计算机内表示小数时(包括float和double型小数)都有误差。判断两个小数是否相等,只能判断他们之差的绝对值是不是在一个很小的范围内。如果两个数相差很小,就可以认为它们相等。这就是我们定义函数equal的原因。
代码如下:
#include <iostream>
using namespace std;
bool invaluedInput = false;
/*因为计算机内表示小数时(包括float和double型小数)都有误差。判断两个小数是否相等,
只能判断他们之差的绝对值是不是在一个很小的范围内。如果两个数相差很小,就可以认为它们相等。*/
bool equal(double num1,double num2)
{
if(num1-num2>-0.0000001 && num1-num2<0.0000001)
return true;
else
return false;
}
double Power(double base,int exponent)
{
if(equal(base,0.0) && exponent<0)
{
invaluedInput = true;
return 0.0;
}
if(0 == exponent)
return 1;
double result = 1.0;
unsigned int absExponent;
if(exponent < 0)
absExponent =(unsigned int)(-exponent);
else
absExponent = (unsigned int)exponent;
int i;
for(i=1; i<=absExponent; i++)
{
result *= base;
}
if(exponent < 0)
return (1.0/result);
else
return result;
}
int main()
{
double num = Power(0,-4);
if(!invaluedInput)
cout<<num<<endl;
}
如果我们输入的指数exponent为32,按照前面的算法,我们在函数Power中的循环中至少需要做乘法31次。但我们可以换一种思路考虑:我们要求出一个数字的32次方,如果我们已经知道了它的16次方,那么只要在16次方的基础上再平方一次就可以了。而16次方是8次方的平方。这样以此类推,我们求32次方只需要做5次乘法:求平方,在平方的基础上求4次方,在4次方的基础上平方求8次方,在8次方的基础上求16次方,最后在16次方的基础上求32次方。
也就是说,我们可以用如下公式求a的n次方:
这个公式很容易就能用递归来实现。
代码如下:double Powerdouble base, unsigned int exponent)
{
if(exponent == 0)
return 1;
if(exponent == 1)
return base;
double result = Power(base, exponent >> 1);
result *= result;
if(exponent & 0x1 == 1)
result *= base;
return result;
}
我们用右移运算符代替了除以2,用位运算符代替了求余运算符(%)来判断一个数是奇数还是偶数。位运算的效率比乘法及求余运算的效率要高很多。既然要优化代码,我们就把优化做到极致。
上面的代码没有考虑溢出:其实一般考考虑溢出可以用下面的方法:
1.
有符号int型能表示的最大值为
0x7fffffff,数值为:
2147483647,最小值为:
0x80000000,数值为:-2147483648。
2.如果number有趋于变大的趋势,比如number += number2,number *= number2,这时如果运算结果比计算前的结果小就可认为是正溢出了
int number1;
int number2;
int tmp;
int i = 0;
for(i; i<n; i++)
{
tmp = number1;
number1 += number2;
if(number1 < tmp)
{
overfload = true;
return ;
}
}
如果number有趋于变小的趋势,比如number -= number2,这时如果运算结果比计算前的结果大就可认为是负溢出了
int number1;
int number2;
int tmp;
int i = 0;
for(i; i<n; i++)
{
tmp = number1;
number1 -= number2;
if(number1 > tmp)
{
overfload = true;
return ;
}
}