Converting a float to a fraction as a string
There isn't much to say about the function and algorithm it self. It's an ordinary recursive function - with tail call - which is optimal for recursive functions.
I don't find a string as return value really useful. Instead I think, I would return a tuple of two integers representing the numerator and denominator
let toFraction (num: float): (int * int) = etc...

Alternatively you could define a discriminated union as something like:
type Fraction = Fraction of int * int

... used like:
let toFraction num =
if num &lt;= 0.0 || num &gt;= 1.0 then
    failwith &quot;Invalid input&quot;
else
  let rec farey n0 d0 n1 d1 =
    let n2 = n0 + n1
    let d2 = d0 + d1

    match float n2 / float d2 with
    | x when abs (x - num) &lt; 1e-10 -&gt; Fraction(n2, d2)
    | x when x &lt; num -&gt; farey n2 d2 n1 d1
    | x when x &gt; num -&gt; farey n0 d0 n2 d2
    | _ -&gt; failwith &quot;Something went completely wrong&quot; // just to fulfill the pattern matching - it should never happen

  farey 0 1 1 1

and called as
let (Fraction(num, denom)) = toFraction value
printfn &quot;%d / %d&quot; num denom

As shown, I've chosen to run farey with integers instead of floats. You should test if that is more efficient than using floats.


match (n2 / d2) with

You don't need the parentheses.


num is in the set 0 &lt;= num &lt; 1

0 = num is an edge case that is calculated wrongly to
1 / 100001

If you want 0 to be included in the domain, you need to start faray with the values -1 0 1 1. Then it will return &quot;0 / 1&quot;.

Even though you specify in the comment, that it is assumed that num should be between zero and one, you should guard against invalid input, especially because invalid input may result in an infinite recursive loop.