数值的整数次方

本文转自:http://zhedahht.blog.163.com/blog/static/254111742009101563242535/

题目:实现函数double Power(double base, int exponent),求baseexponent次方。不需要考虑溢出。

分析:这是一道看起来很简单的问题。可能有不少的人在看到题目后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;
}

如果我们输入的指数exponent32,按照前面的算法,我们在函数Power中的循环中至少需要做乘法31次。但我们可以换一种思路考虑:我们要求出一个数字的32次方,如果我们已经知道了它的16次方,那么只要在16次方的基础上再平方一次就可以了。而16次方是8次方的平方。这样以此类推,我们求32次方只需要做5次乘法:求平方,在平方的基础上求4次方,在4次方的基础上平方求8次方,在8次方的基础上求16次方,最后在16次方的基础上求32次方。

也就是说,我们可以用如下公式求an次方:

程序员面试题精选100题(44)-数值的整数次方 - 何海涛 - 微软、Google等面试题

这个公式很容易就能用递归来实现。

代码如下:
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 ;
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值