Rewritten implementation of quadratic equation formula
Aside from re-computing discriminant(), a reasonable quadratic implementation.



When assessing the quality of floating-point computations, use "%e" rather than "%f".  I recommend using "%e" with enough precision too.  Like "%.17e", or use one of the DECIMAL_DIG family of constants.



Precision improvement when b*b near discriminant:

When |b| is about sqrt(discriminant), the computation of root1 or root2 will cancel many digits.  To avoid this, consider that a*root1*root2 = c

// calculate root1 and root2 together.
if (b &lt; 0) {
  root1 = (-b + sqrt(discriminant))/(2*a);
  root2 = c/(root1*a);
} else {
  root2 = (-b - sqrt(discriminant))/(2*a);
  root1 = c/(root2*a);
}


Try a=1.0, b = -1e24-1, c = 1e24.  Roots should be 1e24, 1 as here.  OP's approach  results in 1e24, 0, total loss of precision.  Keep in mind, FP has overall logarithmic precision, not linear.

When b*b and discriminant greatly differ, this approach is not significantly weaker than OP's original.

Precision improvements are possible with b * b - 4 * a * c, yet tend to impact performance more.



Advanced concern:

root1(), root2() is on thin ice at it relies on a non-negative result from discriminant().

C is sneaky in that FP code is allowed to use higher than needed precision selectively.  See FLT_EVAL_METHOD.  

With OP's code, this certainly will not happen as is, yet with a subtle change of in-lining b * b - 4 * a * c; instead of an explicit function call, may yield the following:

A test of b * b - 4 * a * c &lt; 0 may be false yet the later (-b + sqrt(b * b - 4 * a * c)) may attempt a square root of a negative value due to different optimizations.  

Solution: Use the same result rather than re-compute discriminant.  Do not rely on "same looking code" to generate the exact same FP result especially when near 0.0.

This also eliminates the wasteful re-computation of discriminant().



Check spelling on "coefficents": I'd expect "coefficients".



To meet OP's goal to split the functions into 3: discriminant, first root, other root consider: 

double discriminant(double a, double b, double c);
double first_root(double discriminant, double a, double b);
double other_root(double root1, double a, double c);

...
doubled = discriminant(a, b, c);
if (d &lt; 0) ...
else if (d == 0) ....
else {
  double root1 = first_root(discriminant, a, b);
  double root2 = other_root(root1, a, c);
  ...
}