如何定义避免无符号整数乘法溢出的最大阈值SOME_BIG_VALUE?
首先咱们得把核心需求理清楚:你要找一个最大的SOME_BIG_VALUE,保证任意两个不超过它的unsigned int相乘时,结果不会溢出(也就是乘积不超过unsigned int的最大值UINT_MAX)。这样后续你判断乘积是否超过阈值时,就能安全用a*b计算而不用担心溢出,一旦超过就把结果设为0代表“过大的值”。
关键原理
对于无符号整数来说,unsigned int的最大值是UINT_MAX(定义在<limits.h>头文件里)。要让两个数a和b(都≤S)相乘不溢出,等价于S*S ≤ UINT_MAX——因为如果S满足这个条件,那任何两个≤S的数相乘,结果肯定不会超过UINT_MAX;要是S再大一点,比如S+1,那(S+1)*(S+1)就会超过UINT_MAX,触发溢出。
所以SOME_BIG_VALUE就是UINT_MAX的整数平方根,也就是最大的整数S,使得S*S ≤ UINT_MAX。
具体实现方式
有几种干净的定义方式,你可以根据场景选:
通用编译时计算(推荐)
利用C标准库的平方根函数,结合UINT_MAX来定义,适配所有位数的unsigned int:#include <limits.h> #include <math.h> #define SOME_BIG_VALUE ((unsigned int)sqrt(UINT_MAX))这里
sqrt(UINT_MAX)返回的浮点数,强制转换成unsigned int后就是我们要的最大整数阈值。因为UINT_MAX对于32位或64位unsigned int来说,其值都在double的精确表示范围内(double能精确表示2^53以内的整数),所以转换不会有精度问题。针对固定位数的直接定义
如果你的代码只针对特定位数的unsigned int(比如常见的32位),可以直接写死数值,不用依赖数学库:// 32位unsigned int的UINT_MAX是4294967295,平方根约为65535.999,取整后为65535 #define SOME_BIG_VALUE 65535U纯整数编译时宏(无库依赖)
要是不想依赖<math.h>,可以用位运算来实现(仅适配位数为2的幂次的unsigned int):#include <limits.h> // 对于n位的unsigned int,右移n/2位就能得到平方根的整数部分 #define SOME_BIG_VALUE (UINT_MAX >> (sizeof(unsigned int) * 8 / 2))比如32位
unsigned int,sizeof(unsigned int)*8是32,除以2是16,右移16位得到65535,刚好符合要求;64位的话右移32位得到4294967295,其平方刚好小于64位unsigned int的最大值,不会溢出。
验证逻辑
当你用上文的方式定义SOME_BIG_VALUE后,只要a和b都≤这个值,a*b的结果肯定≤UINT_MAX,不会触发无符号整数溢出。之后你就可以安全执行原来的逻辑:
unsigned c = a * b; if (c > SOME_BIG_VALUE) { c = 0; }
这样既保证了所有乘法操作都不会溢出,又能把超过阈值的结果统一标记为0。
备注:内容来源于stack exchange,提问作者Arnaud

