I posted a topic on Stack overflow and have received no answers, so I'm trying here.
The post is:

It relates to a function that I'd like to use in a project for a microscope illuminator. The function will serve nicely as a transfer function between input and output where the sensitivity can be controlled by the slope. I've already developed function for LINear (easy), LOG, and EXP. The function I'm interested in porting to C++ is originally from here and behaves essentially as a inverse sigmoid (Logit) function:

The beauty of the above example is that it anchors at (0,0), (0.5,0.5) and (1,1) making it ideal for a first-stage transfer function with easy remapping to PWM values to operate the LED illuminator.

The problem, described in the Stack overflow question, is that the denominator soon becomes the root of a negative number, yielding imaginary number solutions.

My first step is to try it in Excel (actually LibreOffice Calc) before porting to C++ for the ESP32, but I'm stuck with the -ve root.
Can anyone help?
Thanks,
Ric

Is the area where the denominator is imaginary of any interest to you? It seems like that would only be for values of x larger than sqrt(k) + 0.5. If you're only looking at the range from 0-1 then you could simply test to exclude those values.

Hi,
That's exactly the point. I don't want imaginary numbers.
Assume a display shows "brightness" as 0-100%. This maps to 0-255 for 8 bit resolution or 0-511for 9 bit resolution (etc) PWM. With a linear scaling factor of 2.55 or 5.12, integer arithmetic can drive the PWM values presented to the ESP32.

LED illumination is non-linear to the eye. In addition the LED illuminator is used to provide light for photographic exposure.

A LOG function flattening towards (1,1) will show greatest sensitivity at higher luminous values, an EXP function will show greatest sensitivity at low values of illumination where the gradient is least. An inverse sigmoid function flattens to the middle, allowing greatest sensitivity at mid-range values.

The other functions are all tested and working fine but the LOGIT function is hardest to achieve. The very clever algorithm in the link simulates the logit curve altering the gradient around the mid-point but not the anchors at (0,0) and (1,1).

The shape of the curve is ideal. But I need it to deal with real (0-100%) input values and real 0 --> whatever PWM values. And that's where the denominator is tricky for me to work with. I can't find an easy solution.

At least until I read a later post in this same thread. The interval is 0-1 for the input and not 0-100. I need to scale after the function using the y-values and not before by exceeding the input range 0-1.

When I want to perform an interpolation between point A and point B using an initial vector Va and an ending vector Vb, I often use a cubic Bézier curve.

The formula is defined by four control points (commonly found in vector drawing software): P0 (start point, A), P1, P2, and P3 (end point, B).

where t represents the progress along the curve, it is a parameter ranging from 0 to 1. the formula only uses addition and multiplication

In the case of 2 points and 2 vectors (initial velocity, ending velocity), P0 corresponds to A, P3 corresponds to B, and the vectors Va and Vb are used to define the tangents at points A and B.

P0 = A(Xa, Ya)
P1 = A + Va
P2 = B - Vb
P3 = B(Xb, Yb)

by changing the initial and ending vectors you can define the shape of your curve

With A and B anchored at (0,0) and (1,1) I'd only have to specify four parameters, namely the size and direction of each of the two vectors. This greatly simplifies the maths and would reduce the program size.

It's straightforward to 'condition' the inputs for 0<=x<=1 (eq'n of the form y=mx+c) and to further condition the output to scale for the max PWM output (2^resolution - 1) and adjust for a threshold value (the PWM output below which the LED is not illuminated); the latter depending, of course, on the LED and series resistor. Again the output conditioning is little more than a variation of y=mx+c with the threshold determining the intercept.

A further variation, the sigmoid curve, has no direct use for me but a friend is developing a 6-axis robot arm where the sigmoid function would provide a nice "soft start" and "soft landing" at the limits of travel:

I like the universality of your approach so thanks very much for the suggestion.
Ric