1、用const关键字定义
const是constant的英文简写,用于定义常量,const定义的常量放在内存只读存储区。格式:const 类型名 标识符 = _____;
π是个常数,所以应用const关键字声明常数。有些书中这样定义π:
const double pi = 3.14;
这样的代码在算法竞赛中是有问题的,因为它只精确到两位小数,而算法竞赛中经常会要求结果保留n位小数,如果n>2显然就会输出错误的结果。
完美的代码是这样的:
const double pi = acos(-1.0);
因为cos(π)=-1,所以反余弦函数acos(-1.0)值就是π [注:反余弦函数acos(x)会返回一个弧度,这个弧度的余弦值等于x。]
有一点需要说明的是,如果acos(-1.0)写成acos(-1)也是可以的,而且在https://en.cppreference.com/w/c/numeric/math/sin中给出的模板代码就是这样写的:
但是在刘汝佳《算法竞赛入门经典》书中的代码用的是acos(-1.0),老金也觉得用这种方式是更好的习惯。因为我们脑子里要始终有一根弦:1代表整数,1.0代表浮点数。acos(x)的参数要求是浮点数,所以最好用1.0。如果你习惯了用acos(-1),那么有一天你就可能会写出acos(-1/2),这就会输出错误的结果。因为程序会按“整数/整数=整数”的算法求解,-1/2的计算结果-0.5的整数部分-0,也就是0,故acos(-1/2)= acos(0)= π/2,而acos(-1.0/2)= acos(-0.5)=2π/3。
注:反余弦函数(acos)是余弦函数的反函数,其定义域是[-1, 1],值域是[0, π]。
换句话说,对于任何在[-1, 1]范围内的数x,反余弦函数(acos(x))会返回一个角度,这个角度的余弦值等于x。
我们知道:
cos(0) = 1
cos(π) = -1(π是180度)
由于反余弦函数是余弦函数的反函数,所以:
acos(1) = 0
acos(-1) = π
2、用宏定义π
宏定义就是用标识符号替换常数,它只在编译时起作用,由编译器将代码中的标识符替换为对应的常数,相当于excel里的“替换”。格式:#define 标识符 字符串
注意:后不加分号(;)
在数学标准库中(C定义在math.h,C++定义在cmath)用宏定义的方式定义了π。即将M_PI定义为3.14159265358979323846。也就是说,这个π是标准库中为我们定义好的,如果在程序开头加对应的头文件,我们就可以直接拿“M_PI”当π来用。
#include <stdio.h>
#include <math.h>
int main() {
double pi = M_PI;
printf("Pi is approximately %.2f\n", pi);
return 0;
}
这种方法有个缺陷,如果你忘了宏的名字,就没法用了。
当然也可以自己在代码中直接进行宏定义,比如:
#define PI 3.14
这个方法缺陷和const double pi = 3.14相同,就是精度方面的问题,当然你记住的小数位数足够多的话也不是问题。
总体来说,还是推荐用“const double pi = acos(-1.0);”,除了记忆负担、精度方面的好处之外,这种方式占用的内存也是比宏定义要少的。
举个简单的例子:
代码1:
#include <stdio.h>
#include <math.h>
int main() {
double p1 = M_PI;
double p2 = M_PI;
double p3 = M_PI;
return 0;
}
代码2:
#include <stdio.h>
#include <math.h>
int main() {
const double pi = 3.14;
double p1 = pi;
double p2 = pi;
double p2 = pi;
return 0;
}
同样为三个浮点数变量p1、p2、p3赋值,代码1因为在编译时就将M_PI替换为3.14159265358979323846,运行时需要在内存中分配三块空间,这三块空间实际存储的都是同一个值。代码2是将常量pi传给p1、p2、p3,他们可以共用pi一个地址,只分配一块内存空间。
这就像你去寄存行李一样,double p1 = M_PI就像对老板说:“我要存包,要能放double这么大包的柜子。”老板就会为你开个符合你要求大小的空柜柜子,然后拿出一个柜子号码牌p1给你。显然这种方式你每存一次包,就要开一个空柜子。而double p1 = pi相当于对老板说:“我要取包,我的号码牌是pi”,老板就会打开pi对应的那个柜子,把东西取出来。不管是你还是你七大姑八大姨(p1、p2、p3)来取,只要拿着号码牌(pi),人家就给你开那个柜,把包给你。
注意:自始至终,老板都不关心你这包里是什么,正如系统也不关心你用宏定义存在p1、p2、p3里的M_PI是不是同一个值。