Convert a double to a string containing rational and surd
This is not a good question because the objective is unrealistic. The code (or perhaps the problem statement) is also flawed since √1 is considered a surd, which - if I am not mistaken - is wrong. (A surd is an "irrational nth root of a positive integer (n > 1)", i.e. it has a non-recurring floating point representation.)

Here is why the objective needs sorting out

Reasonably performant algorithms exist to express floating point numbers as irrational numbers (all of which may be expressed as x / y * √1).  Here's an example (E&amp;OE):

static string Frac(double value)
{
  const double compareTolerance = 1.0E-12;
  bool isNegative = value &lt; 0;
  value = Math.Abs(value);

  long numerator = 1L;
  long denominator = 1L;
  double fraction = (double)numerator / denominator;

  while (Math.Abs(fraction - value) &gt; compareTolerance)
  {
    if (fraction &lt; value)
      numerator++;
    else
    {
      denominator++;
      numerator = (long)(value * denominator);
    }
    fraction = (double)numerator / denominator;
  }
  return $"{numerator} / {denominator} ~= {fraction}, error = {value - fraction}";
}


Given Console.WriteLine(Frac(2.0 / 3 * Math.Pow(7, 1.0/3)));

One gets 1238109 / 970847 ~= 1.27528745518089, error = 7.04103442217274E-13

Reducing the "tolerance" leads to finding bigger (numerator, denominator) pairs giving "increasingly accurate" rational equivalents (within the significant digit limit of the floating point type of course). Taking the floating point representation of any rational, multiplying (or dividing) by a (floating point approximation to a) surd, just gives another floating point number which can be (approximately) represented by a rational. Every floating point number therefore has virtually unlimited rational * surd representations, so you need to specify how you want to limit the search range and/or output possibilities.