How to calculate fractional roots on a computer?

70 Views Asked by At

I worked out a closed form formula for a function that is defined for positive integers $n$:

  • When $n$ is even: $x = 1 - (\alpha - 1)^n$
  • When $n$ is odd: $x = (\alpha - 1)^n + 1$

When this is inverted naively into code:

const computeInverseBlendAlpha = (targetOpacity, nCopies) => {
  console.log("computeInverseBlendAlpha", targetOpacity, nCopies);
  return nCopies % 2
    ? (targetOpacity - 1) ** (1 / nCopies) + 1
    : (1 - targetOpacity) ** (1 / nCopies) + 1;
};

It produces garbage values (NaN for odd n and nonsensical $>1$ values for even n).

So, there are strong clues here that it seems to be important to define that I want to limit $0 \le \alpha \le 1$. I can see that e.g. I need to obtain the appropriate $\alpha \le 1$ solution for the even fractional power. There is always a second real solution and javascript is always providing that wrong solution with the exponentiation.

In the odd case, e.g. -0.8 ** 1/3 produces NaN even though it should be $-.9283$... I do suspect that here even the intermediate value $1/3$ is not representable as a binary floating point value, and I could convince myself that it may be nonsensical to raise something to such a power.

Clearly we need to use more sophisticated algorithms than the Math.pow provided by this language to compute the values I am interested in. I'm curious what straightforward techniques I might employ for that. I guess I didn't pay enough attention in high school. I know that the complex roots exist in a circle on the complex plane and if I could get all of those roots then I could pick out the correct real roots. What are some algorithms that can calculate them?

1

There are 1 best solutions below

1
On

In pencil and paper math, you can have something like $(-8)^{1/3}$ and people see that as the same as $\sqrt[3]{-8}$, which is $-2$.

But that required knowing the exponent was a rational number. In general, an exponent is not rational. And in general for most computing systems, $a^b$ means $\exp(b\ln(a))$, and there are methods for computing $\ln(x)$ and $\exp(x)$ that are deep in the language, or even in the hardware, to make them fast. That requires $a$ to be positive. The consequence is you should never allow for a negative base in code like this (unless you know the exponent is a clean integer, where soemthing special can be done).

Actually in this setting, the exponent in $(-8)^{1/3}$ is the floating point $0.33\ldots3$. That is the same as $\frac{33\ldots3}{100\ldots0}$. So this would be taking an even root of $-8$, which is not real. It's just another reason to outlaw using a negative base (except for when the exponent is an integer).

This is the source of your first issue. When $n$ is odd, you ask it to use $x-1$, a negative value, as the base. It returns NaN.

For even $n$, you say it is nonsensical that the result is greater than $1$. Why is that nonsensical? Your expression adds $1$ to $(1-x)^{1/n}$, so it should be greater than $1$. (But I think that expression is not inverting what you started with.)

As noted in the comments, you start with $$x=1-(1-\alpha)^n$$ regardless of $n$'s parity. Solve for $\alpha$ and you have $$\alpha=1-(1-x)^{1/n}$$ So replace

  return nCopies % 2
    ? (targetOpacity - 1) ** (1 / nCopies) + 1
    : (1 - targetOpacity) ** (1 / nCopies) + 1;

with

  return 1 - (1 - targetOpacity)**(1 / nCopies);