Browse Source

math: do not use floats in the polynomial root finding.

undefined
Sam Hocevar 9 years ago
parent
commit
e0698e9600
1 changed files with 27 additions and 19 deletions
  1. +27
    -19
      src/lol/math/polynomial.h

+ 27
- 19
src/lol/math/polynomial.h View File

@@ -151,6 +151,8 @@ struct polynomial
} }
else if (degree() == 3) else if (degree() == 3)
{ {
static T const pi = acos(T(-1));

/* p(x) = ax³ + bx² + cx + d */ /* p(x) = ax³ + bx² + cx + d */
T const &a = m_coefficients[3]; T const &a = m_coefficients[3];
T const &b = m_coefficients[2]; T const &b = m_coefficients[2];
@@ -176,31 +178,31 @@ struct polynomial
* u³ = (-n/a + √((n/a)² + 4m³/27))/2 * u³ = (-n/a + √((n/a)² + 4m³/27))/2
* v³ = (-n/a - √((n/a)² + 4m³/27))/2 * v³ = (-n/a - √((n/a)² + 4m³/27))/2
*/ */
T const delta = (n * n) / (a * a) + 4.f * m * m * m / 27.f;
T const delta = (n * n) / (a * a) + T(4) * m * m * m / T(27);


/* Because 3×u×v = -m and m is not complex /* Because 3×u×v = -m and m is not complex
* angle(u³) + angle(v³) must equal 0. * angle(u³) + angle(v³) must equal 0.
* *
* This is why we compute u³ and v³ by norm and angle separately instead of
* using a std::complex class
* This is why we compute u³ and v³ by norm and angle separately
* instead of using a std::complex class
*/ */
T u3_norm, u3_angle; T u3_norm, u3_angle;
T v3_norm, v3_angle; T v3_norm, v3_angle;


if (delta < 0) if (delta < 0)
{ {
v3_norm = u3_norm = sqrt((-n/a) * (-n/a) + abs(delta)) / 2.f;
v3_norm = u3_norm = sqrt((-n/a) * (-n/a) + abs(delta)) / T(2);


u3_angle = atan2(sqrt(abs(delta)), -n/a); u3_angle = atan2(sqrt(abs(delta)), -n/a);
v3_angle = -u3_angle; v3_angle = -u3_angle;
} }
else else
{ {
u3_norm = (-n/a + sqrt(delta)) / 2.f;
v3_norm = (-n/a - sqrt(delta)) / 2.f;
u3_norm = (-n/a + sqrt(delta)) / T(2);
v3_norm = (-n/a - sqrt(delta)) / T(2);


u3_angle = u3_norm >= 0 ? 0 : M_PI;
v3_angle = v3_norm >= 0 ? 0 : -M_PI;
u3_angle = u3_norm >= 0 ? 0 : pi;
v3_angle = v3_norm >= 0 ? 0 : -pi;


u3_norm = abs(u3_norm); u3_norm = abs(u3_norm);
v3_norm = abs(v3_norm); v3_norm = abs(v3_norm);
@@ -210,22 +212,28 @@ struct polynomial


for (int i = 0 ; i < 3 ; ++i) for (int i = 0 ; i < 3 ; ++i)
{ {
T u_angle = u3_angle / 3.f + i * 2.f * M_PI / 3.f;
T v_angle = v3_angle / 3.f - i * 2.f * M_PI / 3.f;
T u_angle = (u3_angle + i * T(2) * pi) / T(3);
T v_angle = (v3_angle - i * T(2) * pi) / T(3);


solutions[i] = solutions[i] =
pow(u3_norm, 1.f / 3.f) * cos(u_angle) +
pow(v3_norm, 1.f / 3.f) * cos(v_angle);
pow(u3_norm, T(1) / T(3)) * cos(u_angle) +
pow(v3_norm, T(1) / T(3)) * cos(v_angle);
} }


if (delta < 0) // 3 real solutions if (delta < 0) // 3 real solutions
return array<T> {solutions[0] - k, solutions[1] - k, solutions[2] - k};
else if (delta > 0) // 1 real solution
return array<T> {solutions[0] - k};
else if (u3_norm > 0) // 2 real solutions
return array<T> {solutions[0] - k, solutions[1] - k};
else // one triple solution
return array<T> {solutions[0] - k};
return array<T> { solutions[0] - k,
solutions[1] - k,
solutions[2] - k };

if (delta > 0) // 1 real solution
return array<T> { solutions[0] - k };

if (u3_norm > 0) // 2 real solutions
return array<T> { solutions[0] - k,
solutions[1] - k };

// one triple solution
return array<T> { solutions[0] - k };
} }


/* It is an error to reach this point. */ /* It is an error to reach this point. */


Loading…
Cancel
Save