如何在C++自定义分数类中避免SIGFPE及算术运算溢出?
从零实现安全的C++ Fraction类:避免溢出与浮点异常
最近我在自学C++的类设计,打算写一个尽可能完整的Fraction类,核心诉求是给这个类加上足够的防护——既要避免浮点异常(SIGFPE),也要防止整数溢出,而且绝对不能依赖更大的整数类型(毕竟不是所有编译环境都提供比long更大的原生类型,强行用会带来兼容性问题)。
我的核心目标
- 优先以最小的时间/内存开销,在常见算术运算(+、-、*、/、pow、root)中避免溢出和异常
- 当溢出/异常完全无法避免时,至少要能精准检测到情况,而不是让程序直接崩溃
已梳理的溢出判断逻辑(基于long类型)
我先从基础的整数运算溢出判断入手,毕竟分数的运算最终还是要落到分子分母的整数操作上:
加法(a + b,a、b为long)
一开始我只想到了正数的情况:LONG_MAX - b > a就会溢出,但这显然不完整,得区分正负场景:
- 当a、b均为正数:如果
LONG_MAX - a < b,说明相加结果超出long的最大值,溢出 - 当a、b均为负数:如果
LONG_MIN - a > b,说明相加结果小于long的最小值,溢出 - 一正一负相加时,绝对值是抵消的,不会出现溢出
减法(a - b,a、b为long)
减法可以拆解成a + (-b),直接复用加法的判断逻辑:
- 等价于判断
a + (-b)是否溢出,对应场景:- 若a为正、b为负(即
-b为正):检查LONG_MAX - a < (-b) - 若a为负、b为正(即
-b为负):检查LONG_MIN - a > (-b) - 其他场景(比如正减正、负减负)不会出现溢出
- 若a为正、b为负(即
乘法(a * b,a、b为long)
乘法的溢出判断要更细致:
- 若a或b为0:直接返回0,无溢出
- 同号相乘时:
- 正数相乘:如果
a > LONG_MAX / b,说明a*b超过最大值,溢出 - 负数相乘:如果
a < LONG_MIN / b,说明a*b小于最小值,溢出(两个负数相乘得正数,若结果超过LONG_MAX也会溢出)
- 正数相乘:如果
- 异号相乘时:
- 若a为正、b为负:如果
b < LONG_MIN / a,说明a*b小于LONG_MIN,溢出 - 若a为负、b为正:如果
a < LONG_MIN / b,说明a*b小于LONG_MIN,溢出
- 若a为正、b为负:如果
除法(a / b,a、b为long)
除法主要有两个风险点:
- 除以0:直接触发SIGFPE,必须提前检测b是否为0,禁止执行运算
- 特殊溢出场景:
LONG_MIN / (-1)会溢出,因为LONG_MIN的绝对值比LONG_MAX大1,除以-1后结果超出long的范围,必须单独判断这个情况
pow与root运算
这两个运算的溢出风险更隐蔽:
pow:分数a/b的n次方是aⁿ/bⁿ,这里aⁿ和bⁿ都可能溢出,需要分别对分子分母的幂运算做溢出检测root:比如对分数开n次方,首先要确保运算有意义(比如开偶次方时分母分子不能为负),其次要检测开方后的结果是否超出long范围,还要处理非整数结果的情况(比如是否要转为近似分数还是抛出异常)
实现小技巧
- 运算前先约分:对分数的分子分母先求最大公约数(GCD)约分,能大幅降低后续运算的溢出概率
- 自定义异常类:比如实现
FractionOverflowError、DivisionByZeroError,检测到异常时抛出,让调用方可以捕获并处理,而不是直接崩溃 - 优先提前检测:不要依赖运行时的系统信号,提前在代码层做判断,能让程序的行为更可控
内容的提问来源于stack exchange,提问作者Pernoctador
相关产品推荐
相关产品推荐

