运算符重载实现分数运算时乘减组合计算异常排查
问题描述
我用C++实现了一个Fraction类,包含成员变量zaehler(分子)和nenner(分母),通过运算符重载实现分数的加、减、乘、除运算。基础运算及多数组合运算结果正确且能完成约分化简,但在执行乘减组合运算(如d * e - b)时出现结果错误。调试过程中发现减法运算的临时对象dif的zaehler和nenner存在值异常,请问该异常产生的原因是什么?
相关代码
类定义与运算符重载
Fraction::Fraction() { zaehler = 1; nenner = 1; } unsigned int Fraction::ggT(unsigned int a, unsigned int b) { if (b != 0) //b = 0, wenn a % b = 0, bedeutet Division ohne Rest { return ggT(b, a % b); } else { return a; } } unsigned int Fraction::kgV(unsigned int a, unsigned int b) { int facA[30], facB[30]; for (int i = 0; i < 30; i++) { facA[i] = a * (i + 1); facB[i] = b * (i + 1); } for (int i = 0; i < 30; i++) { for (int k = 0; k < 30; k++) { if (facB[k] == facA[i]) { return facA[i]; } } } } Fraction Fraction::operator-(const Fraction& b) { Fraction dif; int temp_zaehler; int zaehler_a = ((float)this->zaehler / this->nenner) * kgV(this->nenner, b.nenner); int zaehler_b = ((float)b.zaehler / b.nenner) * kgV(this->nenner, b.nenner); dif.zaehler = (zaehler_a - zaehler_b); dif.nenner = kgV(this->nenner, b.nenner); if (dif.zaehler < 0) { temp_zaehler = dif.zaehler; dif.zaehler /= ggT(dif.zaehler*-1, dif.nenner); dif.nenner /= ggT(temp_zaehler*-1, dif.nenner); } else { temp_zaehler = dif.zaehler; dif.zaehler /= ggT(dif.zaehler, dif.nenner); dif.nenner /= ggT(temp_zaehler, dif.nenner); } return dif; } Fraction Fraction::operator*(const Fraction& b) { Fraction prod; int temp_zaehler; prod.zaehler = (this->zaehler) * b.zaehler; prod.nenner = (this->nenner) * b.nenner; if (prod.zaehler < 0) { temp_zaehler = prod.zaehler; prod.zaehler /= ggT(prod.zaehler * -1, prod.nenner); prod.nenner /= ggT(temp_zaehler * -1, prod.nenner); } else { temp_zaehler = prod.zaehler; prod.zaehler /= ggT(prod.zaehler, prod.nenner); prod.nenner /= ggT(temp_zaehler, prod.nenner); } return prod; }
主函数相关代码
std::cout << "d * e - b = " << d.getString() << " * " << e.getString() << " - " << b.getString() << " = " << (d * e - b).getString() << "\n";
异常原因分析
- kgV函数存在未定义行为:kgV函数仅在找到两个分母前30倍范围内的公倍数时返回值,若两个分母的最小公倍数超过各自的30倍(例如分母为31和37,最小公倍数为1147,超过31*30=930),循环结束后无返回语句,会返回随机垃圾值,直接导致减法运算中分子、分母计算完全错误。
- 减法运算中浮点数转换导致精度丢失:减法里用
((float)this->zaehler / this->nenner) * kgV(...)计算转换后的分子,float类型仅约6-7位有效数字,当分子分母数值较大时,浮点数计算会出现精度误差,结果非整数,赋值给int时被截断,最终得到错误的分子值。正确做法是用整数运算:先计算最小公倍数kgV_val = kgV(this->nenner, b.nenner),再通过zaehler_a = this->zaehler * (kgV_val / this->nenner)计算,因kgV是分母的公倍数,kgV_val / this->nenner必然为整数,无精度问题。 - ggT函数存在参数溢出风险:当
dif.zaehler为INT_MIN时,dif.zaehler*-1会触发整数溢出(INT_MIN的绝对值比INT_MAX大1),转换为unsigned int后变成极大的错误值,导致ggT计算错误,进而使约分后的分子分母异常。
内容的提问来源于stack exchange,提问作者VRK
相关产品推荐
相关产品推荐

