编程中无穷大常量的设定技巧(ox3f3f3f3f)

本文探讨了在编程中如何合理地设定无穷大值。详细分析了常用的0x7fffffff为何不是一个理想的选择,并介绍了更为精妙的0x3f3f3f3f及其优势,包括不会在加法中导致溢出、可以使用memset初始化等。

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

 

编程中无穷大常量的设定技巧

如 果问题中各数据的范围明确,那么无穷大的设定不是问题,在不明确的情况下,很多程序员都取0x7fffffff作为无穷大,因为这是32-bit int的最大值。如果这个无穷大只用于一般的比较(比如求最小值时min变量的初值),那么0x7fffffff确实是一个完美的选择,但是在更多的情况 下,0x7fffffff并不是一个好的选择。

 

  1. 很多时候我们并不只是单纯拿无穷大来作比较,而是会运算后再做比较,例如在大部分最短路径算法中都会使用的松弛操作:
    if (d[u]+w[u][v]<d[v]) d[v]=d[u]+w[u][v];
    我们知道如果u,v之间没有边,那么w[u][v]=INF,如果我们的INF取0x7fffffff,那么d[u]+w[u][v]会溢出而变成负数, 我们的松弛操作便出错了,更一般的说,0x7fffffff不能满足“无穷大加一个有穷的数依然是无穷大”,它变成了一个很小的负数。
  2. 除了要满足加上一个常数依然是无穷大之外,我们的常量还应该满足“无穷大加无穷大依然是无穷大”,至少两个无穷大相加不应该出现灾难性的错误,这一点上0x7fffffff依然不能满足我们。

所以我们需要一个更好的家伙来顶替0x7fffffff,最严谨的办法当然是对无穷大进行特别处理而不是找一个很大很大的常量来代替它(或者说模拟 它),但是这样会让我们的编程过程变得很麻烦。

在我读过的代码中,最精巧的无穷大常量取值是0x3f3f3f3f,我不知道是谁最先开始使用这个精妙的常 量来做无穷大,不过我的确是从一位不认识的ACMer(ID:Staginner)的博客上学到的,他/她的很多代码中都使用了这个常量,于是我自己也尝试了一下,发现非常好用,而当我对这个常量做更深入的分析时,就发现它真的是非常精巧了。

 

  1. 0x3f3f3f3f的十进制是1061109567,也就是10^9级别的(和0x7fffffff一个数量级),而一般场合下的数据都是小于10^9的,所以它可以作为无穷大使用而不致出现数据大于无穷大的情形。
  2. 另一方面,由于一般的数据都不会大于10^9,所以当我们把无穷大加上一个数据时,它并不会溢出(这就满足了“无穷大加一个有穷的数依然是无穷 大”),事实上0x3f3f3f3f+0x3f3f3f3f=2122219134,这非常大但却没有超过32-bit int的表示范围,所以0x3f3f3f3f还满足了我们“无穷大加无穷大还是无穷大”的需求。
  3. 最后,0x3f3f3f3f还能给我们带来一个意想不到的额外好处:如果我们想要将某个数组清零,我们通常会使用 memset(a,0,sizeof(a))这样的代码来实现(方便而高效),但是当我们想将某个数组全部赋值为无穷大时(例如解决图论问题时邻接矩阵的 初始化),就不能使用memset函数而得自己写循环了(写这些不重要的代码真的很痛苦),我们知道这是因为memset是按字节操作的,它能够对数组清 零是因为0的每个字节都是0,现在好了,如果我们将无穷大设为0x3f3f3f3f,那么奇迹就发生了,0x3f3f3f3f的每个字节都是0x3f!所 以要把一段内存全部置为无穷大,我们只需要memset(a,0x3f,sizeof(a))。

所以在通常的场合下,0x3f3f3f3f真的是一个非常棒的选择。

### 十六进制常量 0x3f 和 0x3f3f3f3f 的区别及用法 #### 数值大小 十六进制常量 `0x3f` 表示的数值为 63,而 `0x3f3f3f3f` 表示的数值为 1,061,109,567[^1]。从数值上看,`0x3f` 是一个较小的数,通常用于表示有限范围内的值或作为位掩码操作的一部分。而 `0x3f3f3f3f` 是一个较大的数,接近于十亿(10^9),在编程中通常被用作“无穷大”的近似值。 #### 在内存初始化中的应用 `0x3f3f3f3f` 可以通过 `memset` 函数快速初始化数组[^4]。由于 `memset` 是按字节操作的,将每个字节设置为 `0x3f` 时,对于一个四字节的整数,其结果就是 `0x3f3f3f3f`。这种方法效率高且简单,适合需要频繁初始化数组的场景,例如动态规划问题中的状态转移矩阵初始化。 #### 溢出特性与适用场景 - **`0x3f`**:由于其数值较小(63),通常不适用于表示“无穷大”。它更多地出现在位运算或特定算法的小范围数值处理中。 - **`0x3f3f3f3f`**:该值在加法运算中不容易溢出,并且满足“无穷大加有穷数仍为无穷大”的需求[^5]。因此,在需要频繁进行加法运算的算法中(如最短路径算法中的松弛操作),`0x3f3f3f3f` 是一个更好的选择。 #### 示例代码 以下是一个使用 `0x3f3f3f3f` 初始化数组并计算最小值的示例: ```cpp #include <cstdio> #include <cstring> const int INF = 0x3f3f3f3f; int main() { int a[10]; memset(a, 0x3f, sizeof(a)); // 快速初始化数组为 INF for (int i = 0; i < 10; ++i) { printf("%d ", a[i]); } return 0; } ``` #### 与标准常量的区别 - **`INT_MAX`**:`INT_MAX` 是 C/C++ 中的标准常量,对应 0x7fffffff,即最大的 32 位有符号整数。然而,`INT_MAX` 在加法运算中容易溢出,导致负数结果,这在动态规划或图论等算法中可能引发错误。 - **`LLONG_MAX`**:`LLONG_MAX` 是更大的常量,适用于需要处理超过 `0x3f3f3f3f` 范围的场景,但不能通过 `memset` 快速初始化数组[^3]。 ### 总结 `0x3f` 和 `0x3f3f3f3f` 的主要区别在于数值大小和应用场景。前者适用于小范围数值或位运算,后者则作为“无穷大”的近似值广泛应用于动态规划、最短路径等问题中。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

xiangyong58

喝杯茶还能肝到天亮,共同进步

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

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

打赏作者

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

抵扣说明:

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

余额充值