something I thought today

Did I just discover a new mathematical formula?

What a weird way of starting a blog... anyways, please join me in my journey of spending time reasoning about complex stuff I know nothing about for no particular reason.

8 min read × 

Let me start with the disclaimer: I am not sure if this is really unknown, I don’t know if it is useful for something, and I could never give a proper proof for it. I’m just a simple developer and far away from being a mathematician; so be nice. Even if this is not new, I’m glad that I managed to figure this out all by myself.

The history

It all occurred in three fases. The first one started at high school one day when I was in a city bus coming back home. My brain usually never leave me alone, so on that day I was playing with car plates numbers as I saw them through the window. I can’t remember exactly how, but I realized that any x integer squared equals to one below it times one above it plus one.

A=x2 B=(x1)(x+1) AB
12=1 0×2=0 10=1
22=4 1×3=3 43=1
32=9 2×4=8 98=1
42=16 3×5=15 1615=1
52=25 4×6=24 2524=1
62=36 5×7=35 3635=1
72=49 6×8=48 4948=1
82=64 7×9=63 6463=1
92=81 8×10=80 8180=1
102=100 9×11=99 10099=1
Chill out; I know this is basic.

Then a few years later (2016, according to a document I found on my drive in which I was exploring this in more detail) I strived to find out if it could be applied to powers other than two and to describe it as a formula. I ended up with two different equations, one for even and another for odd exponents. For some reason I didn’t see at that moment that the one for even exponents could be used for both cases. This is the one we’ll be considering in this article.

Before we jump right in, let’s walk through some relations that helped me to complete this task.

Ax=x2 Bx=AxAx1 Cx=BxBx1=2!
12=1
22=4 41=3
32=9 94=5 53=2
42=16 169=7 75=2
52=25 2516=9 97=2
62=36 3625=11 119=2
72=49 4936=13 1311=2
82=64 6449=15 1513=2
92=81 8164=17 1715=2
102=100 10081=19 1917=2
Nice, right?

Note that when we recursively subtract one value from the previous one, we get at the end the factorial of the exponent. Let’s see another example.

Ax=x5 Bx=AxAx1 Cx=BxBx1 Dx=CxCx1 Ex=DxDx1 Fx=ExEx1=5!
15=1
25=32 321=31
35=243 24332=211 21131=180
45=1024 1024243=781 781211=570 570180=390
55=3125 31251024=2101 2101781=1320 1320570=750 750390=360
65=7776 77763125=4651 46512101=2550 25501320=1230 1230750=480 480360=120
75=16807 168077776=9031 90314651=4380 43802550=1830 18301230=600 600480=120
85=32768 3276816807=15961 159619031=6930 69304380=2550 25501830=720 720600=120
95=59049 5904932768=26281 2628115961=10320 103206930=3390 33902550=840 840720=120
105=100000 10000059049=40951 4095126281=14670 1467010320=4350 43503390=960 960840=120
It was a lot easier to do it in Excel.

After playing for a little while, I got this (again, this originally is the even power version):

x n = 1 + ( x 1 ) × i = 0 n 1 x i   ; x ,   n
It’s getting interesting.

Let’s see it in action.

43 = 1+(41)×i=0314i 43 = 1+3(40+41+42) 43 = 1+3(1+4+16) 43 = 1+321 43 = 1+63 43 = 64
MathML is cool only until you actually start using it.

However, since we have more exponentiation inside the summation, we can do this recursively just to make it look bigger.

43 = 1+(41)×i=0314i 43 = 1+3(40+41+42) 43 = 1+3(40+(1+3(40))+(1+3(40+41))) 43 = 1+3(40+(1+3(40))+(1+3(40+(1+3(40))))) 43 = 1+3(1+(1+3(1))+(1+3(1+(1+3(1))))) 43 = 1+3(1+(1+3)+(1+3(1+(1+3)))) 43 = 1+3(1+4+(1+3(1+4))) 43 = 1+3(5+(1+35)) 43 = 1+3(5+1+15) 43 = 1+321 43 = 64
Why do I do these things to myself?!

The final unfolding

As you can see, the formula has limitations; it would be nicer if it could handle fractional exponents. This is what I’ve been addressing after all these years because I only recollected this recently.

The tip lies in the leading 1; we can express it as x0. This zero exponent is the same zero on the sum lower bound, and both actually are the fractional part of n—which has been only integers up to now.

Applying it, we finally have this beauty:

x n = x n   mod   1 + ( x 1 ) × i = n   mod   1 n 1 x i   ; x ,   n +
I’m feeling so dang smart.

Or if you are not a fan of using non-integers values in sum bounds (I don’t even know if that is strictly correct to be honest), this one is for you:

x n = x n   mod   1 + ( x 1 ) × i = 0 n 1 x i + n   mod   1   ; x ,   n +

But the latter is kinda ugly, so I’ll stick with the former. Judge me.

Let’s put it to the test.

813.25 = 813.25 mod 1+(811)×i=3.25 mod 13.25181i 813.25 = 810.25+80(810.25+811.25+812.25) 813.25 = 3+80(3+243+19683) 813.25 = 3+8019929 813.25 = 3+1594320 813.25 = 1594323
This is the last time I do this. No, I’m not doing the recursive version. That’s it.

It works when each individual calculation (e.g. 811.25) renders a fraction; I simply picked one that doesn’t do that in order to keep things easier to read.

Some code

Here you can check out the equation in Go:

package main

import (
  "fmt"
  "math"
  "math/cmplx"
)

var memo = make(map[float64]map[float64]complex128)

func Pow(x, n float64) complex128 {
  if r1 := memo[x]; r1 != nil {
    if xpown := r1[n]; xpown != 0 {
      return xpown
    }
  } else {
    memo[x] = make(map[float64]complex128)
  }
  // n mod 1
  nmod1 := math.Mod(n, 1)
  // x^(n mod 1)
  xpownmod1 := cmplx.Pow(complex(x, 0), complex(nmod1, 0))
  // (x - 1)
  xminus1 := complex(x-1, 0)
  // n - 1
  nminus1 := n - 1
  // sum
  var sum complex128
  for i := nmod1; i <= nminus1; i++ {
    sum += Pow(x, i)
  }
  // x^n
  xpown := xpownmod1 + xminus1*sum
  memo[x][n] = xpown
  return xpown
}

func main() {
  x := float64(-4)
  n := float64(3.5)
  builtin := cmplx.Pow(complex(x, 0), complex(n, 0))
  custom := Pow(x, n)
  fmt.Printf("           (%v^%v)\n", x, n)
  fmt.Printf("built-in = %v\n", builtin)
  fmt.Printf("  custom = %v\n", custom)
}
// prints:
//            (-4^3.5)
// built-in = (-5.4864176601801346e-14-128i)
//   custom = (-7.83773951454305e-15-128i)

And the exact same thing but in C++ (written for confirmation purposes):

#include <iostream>
#include <complex>
#include <unordered_map>

using namespace std;

unordered_map<double, unordered_map<double, complex<double>>> memo;

complex<double> Pow(double x, double n) {
  if (auto r1 = memo.find(x); r1 != memo.end()) {
    if (auto xpown = r1->second.find(n); xpown != r1->second.end()) {
      return xpown->second;
    }
  }
  // n mod 1
  double nmod1 = fmod(n, 1);
  // x^(n mod 1)
  complex<double> xpownmod1 = pow(complex<double>(x, 0), complex<double>(nmod1, 0));
  // (x - 1)
  complex<double> xminus1 = complex<double>(x-1, 0);
  // n - 1
  double nminus1 = n - 1;
  // sum
  complex<double> sum;
  for (double i = nmod1; i <= nminus1; i++) {
    sum += Pow(x, i);
  }
  // x^n
  complex<double> xpown = xpownmod1 + xminus1*sum;
  memo[x][n] = xpown;
  return xpown;
}

int main() {
  double x = -4;
  double n = 3.5;
  complex<double> builtin = pow(complex<double>(x, 0), complex<double>(n, 0));
  complex<double> custom = Pow(x, n);
  cout << "           (" << x << "^" << n << ")\n";
  cout << "built-in = " << builtin << "\n";
  cout << "  custom = " << custom << "\n";
}
// prints:
//            (-4^3.5)
// built-in = (-5.48642e-14,-128)
//   custom = (-7.83774e-15,-128)

Side note: I chose 43.5 on purpose to show this interesting computational behavior, that when the result has anything to do with complex numbers, the imaginary part is correct, but the real part shows a super small value while it should be zero. Also, since built-in and custom methods are calculated in different ways, they end up being different from each other, however they are at least consistent across these two languages.

That guy has something to share with us about it:

Let him cook.
Let him cook.
So far so good...
So far so good...
Never seen this before, not gonna lie.
Never seen this before, not gonna lie.
Unfortunately it didn’t fit in the previous screenshot.
Unfortunately it didn’t fit in the previous screenshot.
Lots of stuff to inquire...
Lots of stuff to inquire...
What a polite gentleman.
What a polite gentleman.
Jokes aside, this is a good answer indeed. You did it GPT-4o 👌
Jokes aside, this is a good answer indeed. You did it GPT-4o 👌

Final thoughts

Again, it’s been fun to spend some time getting my head around this, no matter what it is. Please let me know what you think about it. I mean it.

See you in the next one!

Matheus Rosin by Matheus Rosin Problem Solver Software Engineer