如何完美定义π?

本文探讨了在编程中使用const关键字声明常数π的正确方式,以及使用宏定义的优点和不足,提倡使用constdoublepi=acos(-1.0)以确保精度和内存效率。同时,通过行李寄存的比喻解释了const和宏定义在内存分配上的差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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是不是同一个值。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

金创想

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值