Originated from this question, I have this little F# code (github) to generate random values according to a normal distribution:
// val nextSingle : (unit -> float32)
let nextSingle =
let r = System.Random()
r.NextDouble >> float32
// val gauss : (float32 -> float32 -> seq<float32>)
let gauss mean stdDev =
let rec gauss ready = seq {
match ready with
| Some spare ->
yield spare * stdDev + mean
yield! gauss None
| _ ->
let rec loop () =
let u = nextSingle() * 2.f - 1.f
let v = nextSingle() * 2.f - 1.f
let s = pown u 2 + pown v 2
if s >= 1.f || s = 0.f then loop() else
u, v, s
let u, v, s = loop()
let mul = (*)(sqrt(-2.f * log s / s))
yield mul u * stdDev + mean
yield! mul v |> Some |> gauss
}
gauss None
To me it seems that this should only call itself in tail call position, ergo never cause a StackOverflowException
when TCO is enabled. But it does when running 64-bit. It does not when running 32-bit (i.e. “Prefer 32-bit” checkbox in project settings).
I'm using .NET Framework 4.5.2 and F# 4.4.0.0.
Can somebody explain what is causing the problem?
Looks like a bug in the compiler's sequence expression compilation mechanism. Here's a simplified repro:
let rec loop r = seq {
if r > 0 then
let rec unused() = unused()
yield r
yield! loop r
}
printfn "%i" (Seq.nth 10000000 (loop 1))
Obviously the presence of the unused recursive definition shouldn't affect whether this generates a stack overflow, but it does.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With