Games101 作业5 求解一元二次方程

Games101 作业5 易惑点1 求解一元二次方程

在Games101 Ray Tracing的第一份作业中,源码中有一段计算一元二次方程的代码,与数学中的常见求根公式略有不同,这里做一些解释,以方便有类似疑惑的童鞋参考理解:

bool solveQuadratic(const float &a, const float &b, const float &c, float &x0, float &x1)
{
    float discr = b * b - 4 * a * c;
    if (discr < 0) return false;
    else if (discr == 0) x0 = x1 = - 0.5 * b / a;
    else {
        float q = (b > 0) ?
            -0.5 * (b + sqrt(discr)) :
            -0.5 * (b - sqrt(discr));
        x0 = q / a;
        x1 = c / q;
    }
    if (x0 > x1) std::swap(x0, x1);
    return true;
}

求解一元二次方程 ax^2+bx+c=0 ,数学上定义其求根公式为:

1. 计算 Δ=b^2-4ac

2. 若 Δ>0 ,则存在两个根: x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}

3. 若 Δ=0 ,则存在一个根: x=\frac{-b}{2a}

4. 若 Δ<0 ,则不存在根

而代码中的解法则略有不同,原因在于Catastrophic cancellation,具体原理现成资料比较多,不重复造轮子,可以参考:

1. PBRT-E3.9-计算误差的分析与管理(一)

2. 关于 C++ 编程的一道题Milo Yip解答

简单来说,就是因为float浮点数存在误差,在一些特殊的情况下,误差会较大,例如两个float a,b数值相近时,则a-b的误差会较大,a+b的误差较小,那么若求根公式中 bΔ 相近,则其中一个根就会产生较大误差(上述链接2中Milo Yip就做了详细解答),为了避免产生误差,就会计算其中一个误差小的根 x_{1} ,再根据韦达定理_百度百科 x_{1}·x_{2}=\frac{c}{a}

来计算另一个未知根 x_{2} 。那么求x_{1}时采用 + 还是 - 呢?

  • Δ>0 时取决于b的正负性:
  1. b\leqslant0 时,设 q=\frac{-b+Δ}{2} ,则 x_{1}=\frac{q}{a} ,根据韦达定理计算 x_{2}= \frac{\frac{c}{a}}{\frac{q}{a}}=\frac{c}{q}
  2. b\geqslant0时,设q=\frac{-b-Δ}{2} ,则 x_{1}=\frac{q}{a} ,根据韦达定理计算 x_{2}= \frac{\frac{c}{a}}{\frac{q}{a}}=\frac{c}{q}
  • Δ=0时,则 x_{1}=x_{2}=\frac{-b}{2a}


最后,通过swap确保 x_{1} < x_{2} ,由上则与代码解法完成对应。

编辑于 2021-05-24 09:36