// unlike cubicRoots in CubicUtilities.cpp, this does not discard // real roots <= 0 or >= 1 static int cubicRootsX(const double A, const double B, const double C, const double D, double s[3]) { int num; /* normal form: x^3 + Ax^2 + Bx + C = 0 */ const double invA = 1 / A; const double a = B * invA; const double b = C * invA; const double c = D * invA; /* substitute x = y - a/3 to eliminate quadric term: x^3 +px + q = 0 */ const double a2 = a * a; const double Q = (-a2 + b * 3) / 9; const double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54; /* use Cardano's formula */ const double Q3 = Q * Q * Q; const double R2plusQ3 = R * R + Q3; if (approximately_zero(R2plusQ3)) { if (approximately_zero(R)) {/* one triple solution */ s[0] = 0; num = 1; } else { /* one single and one double solution */ double u = cube_root(-R); s[0] = 2 * u; s[1] = -u; num = 2; } } else if (R2plusQ3 < 0) { /* Casus irreducibilis: three real solutions */ const double theta = acos(-R / sqrt(-Q3)) / 3; const double _2RootQ = 2 * sqrt(-Q); s[0] = _2RootQ * cos(theta); s[1] = -_2RootQ * cos(theta + PI / 3); s[2] = -_2RootQ * cos(theta - PI / 3); num = 3; } else { /* one real solution */ const double sqrt_D = sqrt(R2plusQ3); const double u = cube_root(sqrt_D - R); const double v = -cube_root(sqrt_D + R); s[0] = u + v; num = 1; } /* resubstitute */ const double sub = a / 3; for (int i = 0; i < num; ++i) { s[i] -= sub; } return num; }
// from SkGeometry.cpp (and Numeric Solutions, 5.6) int cubicRootsValidT(double A, double B, double C, double D, double t[3]) { #if 0 if (approximately_zero(A)) { // we're just a quadratic return quadraticRootsValidT(B, C, D, t); } double a, b, c; { double invA = 1 / A; a = B * invA; b = C * invA; c = D * invA; } double a2 = a * a; double Q = (a2 - b * 3) / 9; double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54; double Q3 = Q * Q * Q; double R2MinusQ3 = R * R - Q3; double adiv3 = a / 3; double* roots = t; double r; if (R2MinusQ3 < 0) // we have 3 real roots { double theta = acos(R / sqrt(Q3)); double neg2RootQ = -2 * sqrt(Q); r = neg2RootQ * cos(theta / 3) - adiv3; if (is_unit_interval(r)) *roots++ = r; r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3; if (is_unit_interval(r)) *roots++ = r; r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3; if (is_unit_interval(r)) *roots++ = r; } else // we have 1 real root { double A = fabs(R) + sqrt(R2MinusQ3); A = cube_root(A); if (R > 0) { A = -A; } if (A != 0) { A += Q / A; } r = A - adiv3; if (is_unit_interval(r)) *roots++ = r; } return (int)(roots - t); #else double s[3]; int realRoots = cubicRootsReal(A, B, C, D, s); int foundRoots = add_valid_ts(s, realRoots, t); return foundRoots; #endif }
int main(int argc, char **argv) { double x; if (argc != 2) return -1; printf("argv[1] is %s\n", argv[1]); x = strtod(argv[1], 0); printf("%f cube root is %f\n", x, cube_root(x)); return 0; }
static int cubicRootsX(double A, double B, double C, double D, double s[3]) { if (approximately_zero(A)) { // we're just a quadratic return quadraticRootsX(B, C, D, s); } if (approximately_zero(D)) { // 0 is one root int num = quadraticRootsX(A, B, C, s); for (int i = 0; i < num; ++i) { if (approximately_zero(s[i])) { return num; } } s[num++] = 0; return num; } if (approximately_zero(A + B + C + D)) { // 1 is one root int num = quadraticRootsX(A, A + B, -D, s); for (int i = 0; i < num; ++i) { if (approximately_equal(s[i], 1)) { return num; } } s[num++] = 1; return num; } double a, b, c; { double invA = 1 / A; a = B * invA; b = C * invA; c = D * invA; } double a2 = a * a; double Q = (a2 - b * 3) / 9; double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54; double Q3 = Q * Q * Q; double R2MinusQ3 = R * R - Q3; double adiv3 = a / 3; double r; double* roots = s; if (approximately_zero_squared(R2MinusQ3)) { if (approximately_zero(R)) {/* one triple solution */ *roots++ = -adiv3; } else { /* one single and one double solution */ double u = cube_root(-R); *roots++ = 2 * u - adiv3; *roots++ = -u - adiv3; } } else if (R2MinusQ3 < 0) // we have 3 real roots { double theta = acos(R / sqrt(Q3)); double neg2RootQ = -2 * sqrt(Q); r = neg2RootQ * cos(theta / 3) - adiv3; *roots++ = r; r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3; *roots++ = r; r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3; *roots++ = r; } else // we have 1 real root { double A = fabs(R) + sqrt(R2MinusQ3); A = cube_root(A); if (R > 0) { A = -A; } if (A != 0) { A += Q / A; } r = A - adiv3; *roots++ = r; } return (int)(roots - s); }
int solve_cubic ( float a, float b, float c, float d, float* x1, float* x2, float* x3 ) { /* * 3 2 * Solve ax + bx + cx + d = 0 * * Return values: 0 - no solution * -1 - infinite number of solutions * 1 - only one distinct root, real, possibly of multiplicity 2 or 3. * returned in *x1 * 2 - only two distinct roots, both real, one possibly of multiplicity 2, * returned in *x1 and *x2 * -2 - only two distinct roots (complex conjugates), * real part returned in *x1, * imaginary part returned in *x2 * 3 - three distinct real roots of multiplicity 1 * returned in *x1, *x2, and *x3. * -3 - one real root and one complex conjugate pair. * real root in *x1, real part of complex conjugate root * in *x2, imaginary part in *x3. * * XXX - this whole scheme is wrong. * Each root should be returned along with its multiplicity. */ double p, q, r, a2, b2, z1, z2, z3, des; *x1 = *x2 = *x3 = 0.0; if (a == 0.0) return (solve_quadratic (b, c, d, x1, x2)); else { p = b/a; /* reduce to y^3 + py^2 + qy + r = 0 */ q = c/a; /* by dividing through by a */ r = d/a; a2 = (3*q - p*p)/3; /* reduce to z^3 + a2*z + b2 = 0 */ Zero_test2 (a2, 3*q, p*p); b2 = (2*p*p*p - 9*p*q + 27*r)/27; /* using y = z - p/3 */ Zero_test3 (b2, 2*p*p*p, 9*p*q, 27*r); des = (b2*b2/4 + a2*a2*a2/27); Zero_test2 (des, b2*b2/4, a2*a2*a2/27); if (des == 0.0) /* three real roots, at least two equal */ { double a3 = cube_root (-b2/2); if (a3 == 0.0) /* one distinct real root of multiplicity three */ { z1 = 0.0; *x1 = z1 - p/3; Zero_test2 (*x1, z1, p/3); *x2 = 0.0; *x3 = 0.0; return (1); } else /* one real root multiplicity one, another of multiplicity two */ { z1 = 2*a3; z2 = -a3; *x1 = z1 - p/3; Zero_test2 (*x1, z1, p/3); *x2 = z2 - p/3; Zero_test2 (*x2, z2, p/3); *x3 = 0.0; return (2); } } else if (des > 0.0) /* one real root, one complex conjugate pair */ { double d2 = sqrt(des); double t1 = -b2/2 + d2; double t2 = -b2/2 - d2; double a3; double b3; Zero_test2 (t1, b2/2, d2); Zero_test2 (t2, b2/2, d2); a3 = cube_root (t1); b3 = cube_root (t2); z1 = a3 + b3; Zero_test2 (z1, a3, b3); z2 = - z1/2; t1 = a3-b3; Zero_test2 (t1, a3, b3); z3 = sqrt(3.0) * t1/2; *x1 = z1 - p/3; Zero_test2 (*x1, z1, p/3); *x2 = z2 - p/3; Zero_test2 (*x2, z2, p/3); *x3 = z3; return (-3); } else if (des < 0.0) /* three unequal real roots */ { double temp_r, theta, cos_term, sin_term, t1; t1 = b2*b2/4 - des; Zero_test2 (t1, b2*b2/4, des); temp_r = cube_root (sqrt (b2*b2/4 - des)); theta = atan2 (sqrt(-des), (-b2/2)); cos_term = temp_r * cos (theta/3); sin_term = temp_r * sin (theta/3) * sqrt(3.0); z1 = 2 * cos_term; z2 = - cos_term - sin_term; Zero_test2 (z2, cos_term, sin_term); z3 = - cos_term + sin_term; Zero_test2 (z3, cos_term, sin_term); *x1 = z1 - p/3; Zero_test2 (*x1, z1, p/3); *x2 = z2 - p/3; Zero_test2 (*x2, z2, p/3); *x3 = z3 - p/3; Zero_test2 (*x3, z3, p/3); return (3); } else /* cannot happen */ { fprintf (stderr, "impossible descriminant in solve_cubic\n"); return (0); } } }
int cubicRootsReal(double A, double B, double C, double D, double s[3]) { #ifdef SK_DEBUG // create a string mathematica understands // GDB set print repe 15 # if repeated digits is a bother // set print elements 400 # if line doesn't fit char str[1024]; bzero(str, sizeof(str)); sprintf(str, "Solve[%1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]", A, B, C, D); mathematica_ize(str, sizeof(str)); #if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA SkDebugf("%s\n", str); #endif #endif if (approximately_zero(A) && approximately_zero_when_compared_to(A, B) && approximately_zero_when_compared_to(A, C) && approximately_zero_when_compared_to(A, D)) { // we're just a quadratic return quadraticRootsReal(B, C, D, s); } if (approximately_zero_when_compared_to(D, A) && approximately_zero_when_compared_to(D, B) && approximately_zero_when_compared_to(D, C)) { // 0 is one root int num = quadraticRootsReal(A, B, C, s); for (int i = 0; i < num; ++i) { if (approximately_zero(s[i])) { return num; } } s[num++] = 0; return num; } if (approximately_zero(A + B + C + D)) { // 1 is one root int num = quadraticRootsReal(A, A + B, -D, s); for (int i = 0; i < num; ++i) { if (AlmostEqualUlps(s[i], 1)) { return num; } } s[num++] = 1; return num; } double a, b, c; { double invA = 1 / A; a = B * invA; b = C * invA; c = D * invA; } double a2 = a * a; double Q = (a2 - b * 3) / 9; double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54; double R2 = R * R; double Q3 = Q * Q * Q; double R2MinusQ3 = R2 - Q3; double adiv3 = a / 3; double r; double* roots = s; #if 0 if (approximately_zero_squared(R2MinusQ3) && AlmostEqualUlps(R2, Q3)) { if (approximately_zero_squared(R)) {/* one triple solution */ *roots++ = -adiv3; } else { /* one single and one double solution */ double u = cube_root(-R); *roots++ = 2 * u - adiv3; *roots++ = -u - adiv3; } } else #endif if (R2MinusQ3 < 0) // we have 3 real roots { double theta = acos(R / sqrt(Q3)); double neg2RootQ = -2 * sqrt(Q); r = neg2RootQ * cos(theta / 3) - adiv3; *roots++ = r; r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3; if (!AlmostEqualUlps(s[0], r)) { *roots++ = r; } r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3; if (!AlmostEqualUlps(s[0], r) && (roots - s == 1 || !AlmostEqualUlps(s[1], r))) { *roots++ = r; } } else // we have 1 real root { double sqrtR2MinusQ3 = sqrt(R2MinusQ3); double A = fabs(R) + sqrtR2MinusQ3; A = cube_root(A); if (R > 0) { A = -A; } if (A != 0) { A += Q / A; } r = A - adiv3; *roots++ = r; if (AlmostEqualUlps(R2, Q3)) { r = -A / 2 - adiv3; if (!AlmostEqualUlps(s[0], r)) { *roots++ = r; } } } return (int)(roots - s); }