Patashu / break_eternity.js

A Javascript numerical library to represent numbers as large as 10^^1e308 and as small as 10^-10^^1e308. Sequel to break_infinity.js, designed for incremental games.
MIT License
121 stars 44 forks source link

Tetration & super-logarithm not working as expected #108

Closed alemaninc closed 2 years ago

alemaninc commented 2 years ago

It would appear to me that the "tetrate" and "slog" functions are not working as expected.

new Decimal(10).tetrate(1.5) returns 300.2723103062356. I was expecting 1453.0403018990435 (10^10^0.5), but this in itself is not a major issue.

The problem is here: new Decimal(1e9).slog(10) returns 1.9714465989625964, but new Decimal(10).tetrate(1.9714465989625964) returns 1380175625.5753584.

How can that be right?

Patashu commented 2 years ago

Caused by https://github.com/Patashu/break_eternity.js/issues/22 , I think.

If you revert back to https://github.com/Patashu/break_eternity.js/commit/bfb334924427f4f6d413607208ab7d72d303eca3 , it works as expected:

new Decimal(1e9).slog(10).toString() '1.9542425094393248' new Decimal(10).tetrate(1.9542425094393248).toString() '999999999.999996'

The reason why the new changes break this identity is that the old critical section for tetration is literally just exponentially increasing between its value at whole numbers, which isn't analytical in the slightest, just a decent enough approximation (it's monotonic and makes the number go up smooth-ish!).

The literature has advanced to the point, however, where exact values of tetrate and slog to continuous heights can be computed. At that point, we'd have everything we want, right? However, I don't know enough about advanced math to implement it in javascript. I first had a friend plug http://tetration.itgo.com/pdf/TetrationSuperlog_Pages_22-27.pdf into Mathematica, to give me the values that you see in the table at https://github.com/Patashu/break_eternity.js/blob/master/break_eternity.js#L61 . For a set number of bases, I had the exact values of 0.1, 0.2, 0.3, etc. computed. And due to the way the critical section works, this means that 1.1, 2.1, 3.1... etc are also exactly computable. BUT. Slogging 1e9^^1.5 doesn't land us exactly on one of our pre-computed values of slog, so we linearly interpolate between the two nearest values. This leads to losing precision, and a lot of it.

Later, I found a javascript implemented tetration calculator: http://myweb.astate.edu/wpaulsen/tetcalc/tetcalc.html

And it's fully open source (because, well, it's in your browser)! With just two issues, though. One is that it doesn't accept arbitrary bases, and the other is that it doesn't have slog. The former I could just be okay with (most people use 2, e or 10 as a base I would expect, and for others accepting less precision could be excepted). The latter means that no matter how closely I copy the calculator, I'd need to understand how to (or find) a slog calculator, or just pre-compute arbitrarily many values of the slog critical section and hope that it gets me enough precision somehow.

In general, I don't know if I'm smart enough to solve this problem, nor do I know what properties people want more (and which properties people are willing to sacrifice). Definitely curious to hear people's thoughts.

Patashu commented 2 years ago

Oh, and to answer the other question, about Decimal(10).tetrate(1.5) - it DID previously spit out 1453, as you expected:

Old:

new Decimal(10).tetrate(1.5).toString() '1453.0403018990432'

New:

new Decimal(10).tetrate(1.5).toString() '300.2723103062356'

However, http://myweb.astate.edu/wpaulsen/tetcalc/tetcalc.html claims 10^^1.5 is 299.92012356854604298, so I think the new behaviour is closer to ground truth. Also curious as to your thoughts.

Patashu commented 2 years ago

Hmmmm. Hey, I could make an arbitrarily good slog by admitting a number of iterations, and using newton's method (or similar) until we close in on the correct answer (according to however tetrate is calculated). Might do that.

Patashu commented 2 years ago

Okay, that seems to work! Excellent.

Commit:

https://github.com/Patashu/break_eternity.js/commit/4269c743d46b036c6a335ffbabc438f5ae927d9c

Unit test:

for (var i = 0; i < 1000; ++i) { let base = Math.random()10; let tower = Math.random()10; let round_trip = new Decimal(base).tetrate(tower).slog(base).toNumber(); if (Math.abs(round_trip - tower) > 1e10) { console.log(base, tower, round_trip) } }

The new slog runs at about 7e3 calls/s, which is slow, but it's at least slow and right. Open problem as to whether it can be made to converge faster (I bet it can if you're clever).

One last thing I want to do, investigate the tetration critical section to see if intermediate values can be a little more accurate.

Patashu commented 2 years ago

https://github.com/Patashu/break_eternity.js/commit/4237b41876f80a2bf5cc037ca4a01d81dffa8971

Excellent, log-linear approximation-pow is much closer to the ground truth than just linear approximation.

Obviously better could be done (obviously the project of 'merge http://myweb.astate.edu/wpaulsen/tetcalc/tetcalc.html into break_eternity.js' remains open, e.g.) but this bugged me for a long time and I'm glad to finally look into it and prove that this simple improvement helps.

As always, let me know if you have any concerns, questions or suggestions.

Patashu commented 2 years ago

I think everything works now, but feel free to re-open if you find a failing example.