# Finding all zeros of a univariate function on the real line

**URL:** https://discourse.julialang.org/t/finding-all-zeros-of-a-univariate-function-on-the-real-line/132565
**Category:** Numerics
**Tags:** question
**Created:** [September 22, 2025, 12:38pm UTC](https://discourse.julialang.org/t/finding-all-zeros-of-a-univariate-function-on-the-real-line/132565 "2025-09-22T12:38:51Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [September 22, 2025, 12:38pm UTC](https://discourse.julialang.org/t/finding-all-zeros-of-a-univariate-function-on-the-real-line/132565/1 "2025-09-22T12:38:52Z")

</div>

I am trying to find all zeros of

f(z) = C\_m + D\_m H(C\_f + D\_f H(z)) - z

where C\_i, D\_i \in \mathbb{R} (can be any sign) and H is the CDF of the standard normal. If someone wants to experiment, this can be coded in Julia as

```julia
using StatsFuns
makef(Cm, Dm, Cf, Df) = z -> Cm + Dm * normcdf(Cf + Df * normcdf(z)) - z

```

Now if D\_m D\_f \le 2 \pi one can prove uniqueness. Existence is guaranteed because f goes to \pm \infty at the edges.

What I am interested in is finding _all_ zeros, for generic/arbitrary arguments.

When that does not hold, eg

```julia
args = (23.449772654974293, -22.97316619909447, 0.6575825044958663, -0.6582958874152536)
makef(args...)

```

![f](https://global.discourse-cdn.com/julialang/original/3X/7/5/75bc632ec131d7ac77f6191e2633c81c17f34837.svg)

Is there a robust algorithm for this? My first thought was [IntervalRootFinding.jl](https://github.com/JuliaIntervals/IntervalRootFinding.jl), but I ran into an [issue](https://github.com/JuliaIntervals/IntervalRootFinding.jl/issues/225) [that MWE has `p = normcdf(z)`, which as a host of numerical problems on its own, see the values above).

---

<div class="post-metadata">

### Author: ![rveltz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rveltz/32/2707_2.png) [@rveltz](https://discourse.julialang.org/u/rveltz)
#### Post date: [September 22, 2025, 2:26pm UTC](https://discourse.julialang.org/t/finding-all-zeros-of-a-univariate-function-on-the-real-line/132565/2 "2025-09-22T14:26:15Z")

</div>

ApproxFun?

---

<div class="post-metadata">

### Author: ![JonasWickman](https://avatars.discourse-cdn.com/v4/letter/j/9de0a6/32.png) [@JonasWickman](https://discourse.julialang.org/u/JonasWickman)
#### Post date: [September 22, 2025, 3:18pm UTC](https://discourse.julialang.org/t/finding-all-zeros-of-a-univariate-function-on-the-real-line/132565/3 "2025-09-22T15:18:50Z")

</div>

Not a solution, but you should be able to restrict your search space by noting that if f = 0, then

z = C\_m + D\_mH

and since H \in [0,1] (since it’s a cdf), this means that the root z must be in the interval [C\_m, C\_m + D\_m] (if D\_m is positive) or [C\_m + D\_m, C\_m] (if D\_m is negative).

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [September 22, 2025, 3:27pm UTC](https://discourse.julialang.org/t/finding-all-zeros-of-a-univariate-function-on-the-real-line/132565/4 "2025-09-22T15:27:25Z")

</div>

FWIW, you could use AccessibleModels.jl to explore the problem graphically:

```julia-auto
using AccessibleModels, IntervalSets
using GLMakie
using StatsFuns

struct makef{A, B, C, D}
    Cm::A
    Dm::B
    Cf::C
    Df::D
end

(m::makef)(z) = m.Cm + m.Dm * normcdf(m.Cf + m.Df * normcdf(z)) - z

amodel = AccessibleModel(makef(1., 1., 1., 1.),
( (@o _.Cm) => -100..100,
    (@o _.Dm) => -100..100,
    (@o _.Cf) => -100..100,
    (@o _.Df) => -100..100,
))

fig = Figure()
obj, = SliderGrid(fig[1,1], amodel)

lines(fig[2,1], -100..100, @lift x -> $obj(x))

```

And if someone can add input text boxes to the example, it would be awesome.

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [September 23, 2025, 1:23pm UTC](https://discourse.julialang.org/t/finding-all-zeros-of-a-univariate-function-on-the-real-line/132565/5 "2025-09-23T13:23:32Z")

</div>

> [@JonasWickman](#):
>
> you should be able to restrict your search space

This is a great idea! I can take this further and define

y = \frac{z - C\_m}{D\_m}

and then my problem is

H(C\_f + D\_f H(C\_m + D\_m y)) = y

where we know that y \in [0,1] so there is a bracket already. Brent’s method from Roots.jl is pretty good on this, not as fast as Newton’s, but much more robust.

I am only struggling with precision issues where I am around the edges (y \approx 0 or y \approx 1).

> [@rafael.guerra](#):
>
> explore the problem graphically

Thanks, that is really helpful. Now I convinced myself that there is either a unique root or 3 roots. Life would be so much simpler if I got IntervalRootFinding.jl working on this problem.

---

<div class="post-metadata">

### Author: ![abulak](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/abulak/32/28314_2.png) [@abulak](https://discourse.julialang.org/u/abulak)
#### Post date: [September 26, 2025, 10:23pm UTC](https://discourse.julialang.org/t/finding-all-zeros-of-a-univariate-function-on-the-real-line/132565/6 "2025-09-26T22:23:51Z")

</div>

Since the interval for `y` is bounded you can use `isolate_roots` form [GitHub - Joel-Dahne/ArbExtras.jl: Julia package with extra tools for Arblib.jl](https://github.com/Joel-Dahne/ArbExtras.jl) which will either _prove_ to you that all roots are isolated or indicate which ones were not.

You might define `normcdf` as:

```julia
julia> function normcdf!(out::Arb, x::Arb)
          t = Arb(2, prec = precision(x))
          sqrt2 = Arblib.sqrt!(t, t)
          Arblib.inv!(t,t)
          Arblib.mul!(t, t, x)
          Arblib.hypgeom_erf!(out, t)
          Arblib.add!(out, out, 1)
          Arblib.div!(out, out, 2)
          out
       end;

julia> StatsFuns.normcdf(a::Arb) = (x = zero(a); normcdf!(x, a); x);

julia> normcdf(2) - normcdf(Arb(2))
[-1.3849763108389696060605952972944153160163339998695237096472e-18 +/- 3.28e-77]

```

note that to use `isolate_roots` you’d need to define `normcdf` also for `ArbSeries`, which I’m not familiar with; If you’re interested ask `@joeldahne` about this!
