28

I'd like to solve $ax^3 + bx^2 + cx + d = 0$ using the cubic formula.

I coded three versions of this formula, described in three sources: MathWorld, EqWorld,
and in the book, "The Unattainable Attempt to Avoid the Casus Irreducibilis for Cubic Equations".

While I get identical results across all versions, these results are incorrect.

For example, for $a=1$, $b=2$, $c=3$, $d=4$,

I find incorrect roots:
$x_1 = -0.1747 - 0.8521i$,
$x_2 = 0.4270 + 1.1995i$,
$x_3 = -2.2523 - 0.3474i$.

The correct roots are:
$x_1 = -1.6506$,
$x_2 = -0.1747 + 1.5469i$,
$x_3 = -0.1747 - 1.5469i$

In case you're interested, the actual code is below. Thank you for your help!

%% Wolfram version

Q = (3*c - b^2) / 9;
R = (9*b*c - 27*d - 2*b^3) / 54;

D = Q^3 + R^2;
S = (R + sqrt(D))^(1/3);
T = (R - sqrt(D))^(1/3);

x1 = - b/3 + (S + T);
x2 = - b/3 - (S + T) / 2 + sqrt(-3) * (S - T) / 2;
x3 = - b/3 - (S + T) / 2 - sqrt(-3) * (S - T) / 2;

%% Book version

omega1 = - 1/2 + sqrt(-3)/2;
omega2 = - 1/2 - sqrt(-3)/2;

p = (3*a*c - b^2) / (3*a^2);
q = (2*b^3 - 9*a*b*c + 27*(a^3)*d) / (27*a^3);

r = sqrt(q^2/4 + p^3/27);
s = (-q/2 + r)^(1/3);
t = (-q/2 - r)^(1/3);

x1 =        s +        t - b/(3*a);
x2 = omega1*s + omega2*t - b/(3*a);
x3 = omega2*s + omega1*t - b/(3*a);

%% Eqworld version

p = - 1/3  * (b/a)^2 + (c/a);
q =   2/27 * (b/a)^3 - (b*c)/(3*a^2) + d/a;

D = (p/3)^3 + (q/2)^2;
A = (-q/2 + sqrt(D))^(1/3);
B = (-q/2 - sqrt(D))^(1/3);

y1 = A + B;
y2 = - 1/2 * (A + B) + sqrt(-3)/2 * (A - B);
y3 = - 1/2 * (A + B) - sqrt(-3)/2 * (A - B);

x1 = y1 - b / (3*a);
x2 = y2 - b / (3*a);
x3 = y3 - b / (3*a);
PQR
  • 312
  • I haven't looked into your solution carefully, but a common error in cases like this is to inadvertently use integer division instead of fractional division, which is an easy mistake in many programming systems. Is it possible that you've done this? Can you check omega1 and make sure the -1/2 didn't get dropped? – MJD May 17 '17 at 14:32
  • Thank you for your help.This part seems Ok (I'm using Matlab). – PQR May 17 '17 at 14:44
  • 4
    For purposes of numerical stability, you might be interested in this formulation. – J. M. ain't a mathematician May 17 '17 at 21:02
  • I think this is a great alternative; perhaps one downside is the need to compute trigonometric functions. – PQR May 18 '17 at 02:15
  • 1
    Akim, it's a tradeoff (which happens often in programming applications): in casus irreducibilis, you have the choice of either avoiding complex arithmetic and using trigonometry, or using only radicals and arithmetic, but won't be able to avoid complex evaluations. – J. M. ain't a mathematician May 18 '17 at 07:13

2 Answers2

44

The problem occurs when you are taking a cube root: in the Wolfram version, when you compute $$T = \sqrt[3]{R - \sqrt{D}} = \sqrt[3]{-\frac{35}{27}-\sqrt{\frac{50}{27}}}$$ and in corresponding places in the other versions of the cubic formula.

The expression inside the cube root is negative (approximately $-2.66$), and the cubic formula works if you take the real cube root (approximately $-1.39$). But writing $(R - \sqrt D)^{1/3}$ in many computer algebra systems instead computes the principal cube root: the complex root with largest real part. This will be the real cube root for a positive number, but here, it gives us approximately $0.69 - 1.2 i$, and that is the source of your error.

I can't identify the language you're using by looking at it, so I don't know what the best way to avoid this problem is. In Mathematica, there's a built-in CubeRoot command, which will always take the real root of a real number.

You can always make it go with some conditionals: define $T$ to be

  • $(R - \sqrt{D})^{1/3}$, if $R - \sqrt D \ge 0$, and
  • $-(\sqrt D - R)^{1/3}$, if $R - \sqrt D < 0$.

(And do a similar thing everywhere else you take a cube root.)


The above will work for real coefficients; for complex coefficients (or even when $D$ is negative), we want to be more careful, because then a real root might not exist.

The idea is that we ran into trouble here, not because we weren't taking the real root necessarily, but because we took two cube roots that "didn't match up with each other". We can rewrite the cubic formula in a few different ways so that we only take one cube root, and express the other cube root we'd have to take in terms of the first. Then the issue doesn't occur.

This gives us a better way to solve the problem in code: after computing $S = (R + \sqrt D)^{1/3}$, set $T = -Q/S$. The justification is this: since $S^3 = R + \sqrt D$ and $T^3 = R - \sqrt D$, we have $S^3 T^3 = R^2 - D = -Q^3$. Naively, we can cancel the cube roots and assume $ST = -Q$, and this works out.

See also this question and its answer.

Misha Lavrov
  • 159,700
  • 1
    Thank you so much for this! I'm using Matlab but ultimately will convert the code to C. Let me check and see how I can fix it. – PQR May 17 '17 at 14:47
  • 2
    In Matlab, it seems like the nthroot command takes real roots, and C has the cbrt command. – Misha Lavrov May 17 '17 at 14:50
  • Thanks Misha! I will be able to test this properly in a couple of hours. – PQR May 17 '17 at 14:52
  • Good explanation. Wikipedia's solution in terms of the cycles of ${\zeta}^k$ is a little friendlier as well in this regard, because then it doesn't matter which cube root is chosen (so long as it is chosen consistently). –  May 17 '17 at 15:13
  • 1
    I agree, it seems most straightforward to proceed with $ST = - Q$. It's interesting that this ambiguity is not mentioned in any of the original sources. – PQR May 17 '17 at 16:54
  • @Akim In Mathematica, $x^{1/3}$ is understood as the principal third root of $x$. To get the real-valued third root, you can instead use CubeRoot[x] or the more general Surd[x, 3]. – user170231 May 17 '17 at 20:42
  • @Akim, since Mathematica always assumes everything is complex unless explicitly told otherwise, roots are always assumed to take the principal value; as 170231 says, use Surd[] if you want the real root and not the principal root of a negative number. – J. M. ain't a mathematician May 17 '17 at 21:00
-2

This does not look right.

y2 = - 1/2 * (A + B) + sqrt(-3)/2 * (A - B);
y3 = - 1/2 * (A + B) - sqrt(-3)/2 * (A - B);

Did you mean cbrt(-3)?