NOIP学习小站
西安交通大学附属中学航天学校

数学运算

编写程序解决问题,往往离不开运算,从最简单的数学运算,到复杂的幂、开方、三角、对数,C++都能轻松应对。

一、基本数学运算

数学中最基本的是加(+)、减(-)、乘(×)、除(÷)四则运算,C++中有加(+)、减(-)、乘(*)、除(/)、模(%)五则基本运算。下面通过几个示例程序加以说明:

#include<iostream>
using namespace std;
int main()
{
    cout<<123+456<<endl;            //两个整数相加
    cout<<123.456+456.789<<endl;    //两个小数(浮点数)相加
    cout<<123-456<<endl;            //两个整数相减
    cout<<123.456-456.789<<endl;    //两个小数(浮点数)相减
    cout<<-789<<endl;               //-表示负数
    cout<<-(-789)<<endl;            //-也能表示取反
    return 0;
} 

编写并运行上面的程序,观察运行结果,可知用cout输出一个计算表达式,运行时会先计算结果,再将结果输出。再来看下面这段程序:

#include<iostream>
using namespace std;
int main()
{
    cout<<123*456<<endl;   //注意乘号是米字乘*,不能写成叉乘×,也不能写成点乘·
    cout<<123*-456<<endl;
    
    cout<<12.34*56.78<<endl;
    cout<<123.456*456.789<<endl;
    cout<<1234567.45*456.78<<endl;
    cout<<0.00123*0.004567<<endl;
    cout<<123.000<<endl;
    cout<<123.9999<<endl;
    return 0;
} 

在Dev C++中上面程序运行结果如下:

56088
-56088
700.665
56393.3
5.63926e+008
5.61741e-006
123
124

后面6行输出有点费解,和计算器算出来的结果不一致,原因是:cout输出小数(浮点数)时,默认最多保留6位有效数字(第3行输出700.665,第4行输出56393.3的原因);如果数字太大或太小(绝对值超过\(10^6\)或者绝对值小于\(0.00001\)),会按照科学计算法的方式输出(所以第5行输出5.63926e+008、第6行输出5.61741e-0065.63926e+008表示\(5.63926 \times 10^8\),5.61741e-006表示\(5.61741 \times 10^{-6}\))。cout输出浮点数会忽略掉小数末尾的0,所以第7行输出123,第8行保留6位有效数字并忽略掉小数末尾的0输出124。

也可以指定浮点数的输出格式,例如下面的程序指定浮点数保留5位小数输出(注意引入了一个新的头文件#include<iomanip>):

#include<iostream> 
#include<iomanip>   //这个头文件不能少 
using namespace std;
int main()
{    
    //设置输出小数(浮点数)时保留5位小数
	//固定写法,保留其它小数位数修改setprecision()括号中的数字即可 
    cout<<fixed<<setprecision(5);
    //设置后,后面输出都会遵循输出规则 
    
    cout<<12.34*56.78<<endl;
    cout<<123.456*456.789<<endl;
    cout<<1234567.45*456.78<<endl;
    cout<<0.000009<<endl;
    cout<<123<<endl;      //输出123,setprecision设置的保留小数位数对整数无效
    return 0;
} 

程序输出结果如下:

700.66520
56393.34278
563925719.81100
0.00001
123

分析结果可知,保留小数位数的同时还自动实现了“四舍五入”!小结一下:引入iomanip头文件后,使用cout输出浮点数时可以通过cout<<fixed<<setprecision(要保留的小数位数);设置小数位数,保留小数位数时会自动实现“四舍五入”。其实这个保留小数位数的方法过于复杂,不好记忆,后面会介绍简单的方法。

再来看除法运算和模运算(模运算就是求余数):

#include<iostream>  
using namespace std;
int main()
{    
    cout<<5/2<<endl;		// 整数/整数,注意除号必须用/
    cout<<4/2<<endl;		// 整数/整数 
    cout<<5.0/2<<endl;	  // 浮点数/整数
    cout<<5.0/2.0<<endl;	// 浮点数/浮点数
    cout<<5%2<<endl; 	   // %是模运算,这里计算5除以2的余数 
    return 0;
}

程序输出结果如下:

2
2
2.5
2.5
1

分析运算结果,会发现第1行 5/2 输出2与数学运算结果不一致。C++中的除法运算有一个特点,如果是 整数/整数 ,那么结果是一个整数(小数部分全部被舍弃,结果是除法的商),但是被除数或者除数只要有一个是浮点数,那么结果就是浮点数。

注意模运算仅限于 整数%整数 ,否则会出现编译错误!

小结一下:

  1. 整数/整数 ,结果是除法的商;
  2. 浮点数参与除法(被除数数和除数至少有一个是浮点数),结果是浮点数;
  3. 整数%整数 结果是除法的余数。

最后,和数学混合运算一样,C++复杂的运算也涉及到运算优先级的问题:

#include<iostream>  
using namespace std;
int main()
{    
    cout<<1+2*3<<endl;
    cout<<(1+2)*3<<endl;
    return 0;
} 

大家自行编辑程序并运行,通过分析结果可知:C++中也是优先计算乘除(包括模运算),再计算加减;如果有小括号,那优先计算小括号中的内容。这和数学混合运算一致,有一点不同的是,不管多少层括号,都用小括号

二、使用数学函数

来看一个数学问题,计算直角边为60和91的直角三角形斜边长。已知斜边计算公式 \(c = \sqrt{a^2+b^2}\),这里平方和还容易得到:60*60+91*91,但是接下来的开平方是C++的五则基本运算无法实现的!

好在C++中已经内置了不少函数(目前可以把C++的函数理解成可以实现特定运算的工具),能够完成开平方、幂、三角、对数等数学计算。要解决上述问题,我们可以用到两个函数——pow幂函数和sqrt开平方函数,先编写一个程序来测试一下两个函数的功能(注意需要通过#include<cmath>引入cmath头文件):

#include<iostream>
#include<cmath>  //需要引入cmath头文件  
using namespace std;
int main()
{    
    cout<<pow(60,2)<<endl;  //pow(60,2)计算60的2次方,然后cout输出结果
    cout<<sqrt(9)<<endl;    //sqrt(9)计算9开平方,然后cout输出结果
    return 0;
} 

计算斜边的公式 \(\sqrt{60^2+91^2}\),对应的C++代码可以写成:sqrt( pow(60,2) + pow(91,2) ),这个复杂的计算表达式会首先计算sqrt()括号中的内容pow(60,2) + pow(91,2),这是一个加法运算,那么会先后计算出pow(60,2)pow(91,2)的值,然后相加的结果作为sqrt函数开平方的参数。最终程序代码如下:

#include<iostream>
#include<cmath>  //需要引入cmath头文件  
using namespace std;
int main()
{    
    cout<<sqrt(pow(60,2)+pow(91,2))<<endl;
    return 0;
}

其实pow函数可以计算小数次方,由数学知识可知 sqrt(9)pow(9,0.5) 是等效的。那么还可以只使用pow函数解决问题:

#include<iostream>
#include<cmath>  //需要引入cmath头文件  
using namespace std;
int main()
{    
    cout<<pow(pow(60,2)+pow(91,2),0.5)<<endl;
    return 0;
}

这里将cmath头文件中常用的函数整理如下表:

函数调用方法举例说明
\(pow(x,y)\)\(pow(3,2)\)
\(pow(3,0.5)\)
\(pow(2.5,7.8)\)
计算 \(x^y\),结果是浮点数
\(sqrt(x)\)\(sqrt(2)\)
\(sqrt(2.5)\)
计算 \(\sqrt{x}\),结果是浮点数
\(abs(x)\)\(abs(-10)\)计算\(x\)的绝对值\(|x|\)。如果\(x\)是整数,结果是整数;如果\(x\)是浮点数,结果是浮点数
\(fabs(x)\)\(fabs(-10)\)
\(fabs(-2.5)\)
计算\(x\)的绝对值\(|x|\),不论\(x\)是整数还是浮点数,结果都是浮点数
\(ceil(x)\)\(ceil(2.1)\)
\(ceil(-2.9)\)
计算大于或等于\(x\)的最小整数(向上取整),结果是小数部分为0的浮点数
\(floor(x)\)\(floor(2.9)\)
\(floor(-2.1)\)
计算小于或等于\(x\)的最大整数(向下取整),结果是小数部分为0的浮点数
\(sin(x)\)
\(cos(x)\)
\(sin(3.14159)\)
\(cos(3.14159/2)\)
计算三角函数正弦\(sin(x)\)、余弦\(cos(x)\)值
\(x\)是弧度角度
\(exp(x)\)\(exp(1)\)计算\(e^x\),\(e\)是自然常数
\(log(x)\)\(log(10)\)
\(log(exp(1))\)
计算\(x\)的自然对数\(\ln{x}\)(即\(\log_{e}{x}\))
\(log2(x)\)\(log2(2)\)
\(log2(1024)\)
计算以2为底\(x\)的对数\(\log_{2}{x}\)
\(log10(x)\)\(log10(10)\)
\(log10(100)\)
计算以10为底\(x\)的对数\(\log_{10}{x}\)

建议自己编写程序测试一下上面cmath中的常用函数。

此外algorithm头文件下还有\(max、min\)两个常用函数:

函数调用方法举例说明
\(max(a,b)\)\(max(3,5)\)
\(max(0.1,0.5)\)
计算\(a、b\)的最大值
\(a、b\)的类型必须相同,例如全是整数或者全是浮点数
\(min(a,b)\)\(min(3,5)\)
\(min(0.1,0.5)\)
计算\(a、b\)的最小值
\(a、b\)的类型必须相同,例如全是整数或者全是浮点数

三、浮点数的特点

程序设计语言中一般将小数称为浮点数(这个名称是根据浮点数在计算机内部存储方式得来的),来测试下面的程序:

#include<iostream>
#include<cmath>
using namespace std;
int main()
{
	cout<<ceil(1.01)<<endl;
	cout<<ceil(1.000000000000001)<<endl;
    cout<<ceil(1.0000000000000001);
    return 0;
}

ceil是向上取值,预期应该都输出2,但是实际运行结果如下:

2
2
1

感觉奇怪吧!注意:C++中的浮点数只能保证在一定的有效数字范围内是可靠的,超过这个范围就只能认为是约等于不是精确相等

cout<<1.0000000000000001;会输出1,后面的.0000000000000001超出了可靠的有效数字范围,C++处理时这部分被丢弃了!此时cout<<ceil(1.0000000000000001);其实就相当于 cout<<ceil(1);