performance and accuracy of pow()
Several things need to be figured out about pow(). Copying the code as it stands now:
pow(value) {
//UN-SAFETY: We're assuming Decimal^number because number^Decimal or Decimal^Decimal is unheard of in incremental games.
//TODO: Possibly implement slabdrill's generic fast track (with more precise tests)
/*
var temp = Math.pow(this.mantissa,value)
if (Math.abs(temp) < 1.8e307) {
return Decimal.fromMantissaExponent(temp*Math.pow(10,(this.exponent*value)%1), Math.floor(this.exponent*value));
}
*/
//Fast track: If (this.exponent*value) is an integer and mantissa^value fits in a Number, we can do a very fast method.
var temp = this.exponent*value;
if (Number.isSafeInteger(temp))
{
var newMantissa = Math.pow(this.mantissa, value);
if (Number.isFinite(newMantissa))
{
//TODO: This might actually be slower than the 'slow track' if pow is very large, because of the huge amount of normalization we have to do. For example, Decimal.pow(1.43534e-8, 1000) has to be normalized 156 times. Maybe only take fast track if abs(value) <= 10? (Alternatively normalization for very unnormalized numbers can be done)
return Decimal.fromMantissaExponent(newMantissa, temp);
}
}
return Decimal.exp(value*this.ln());
}
sqrt() also has a potential optimization:
static sqrt(value) {
//TODO: If generic fast track pow is not used, implement slabdrill's sqrt and cbrt specific fast track like so:
///if (this.exponent % 2 = 1) return Decimal.fromMantissaExponent(this.mantissa*3.16227766017, Math.floor(this.exponent/2));
value = Decimal.fromValue(value);
return value.sqrt();
}
sqrt, cbrt, sqr, cube are all replaced with their fast track version.
in pow itself, fast track 1 is neutral for performance over random pows, fast track 2 is detrimental for performance. So squeezing out more performance from here may be difficult.
Test used:
for (var i = 0; i < 1000000; ++i)
{
var a = Decimal.randomDecimalForTesting(1000);
var pow = Math.random()*20-10;
if (Math.random()*2 < 1) { pow = Math.round(pow); }
var result = Decimal.pow(a, pow);
}
EDIT: Fast track 2 might become faster if we don't need to normalize() and deliberately skip doing it.
EDIT 2: No good, we do need normalize().
For comparison, SpeedCrunch’s pow is:
https://bitbucket.org/heldercorreia/speedcrunch/src/9cffa7b674890affcb877bfebc81d39c26b20dcc/src/math/floatpower.c?at=master&fileviewer=file-view-default https://bitbucket.org/heldercorreia/speedcrunch/src/9cffa7b674890affcb877bfebc81d39c26b20dcc/src/math/floatipower.c?at=master&fileviewer=file-view-default
- if the exponent is integer, do an integer pow (described in floatipower.c)
- else do exp(ln(x)*exponent)
With no other fast tracks.
Todo:
- Look into if any of the integer pow code can/should be in break_infinity.js and if it speeds things up.
- Test performance of pow 1.5 hitting an integer, pow 1.5 not hitting an integer, pow 2 and random pows with current style and speed crunch style fast track 1. Keep whichever is faster overall.
- Test if sqrt and cbrt optimizations are actually faster or not.
Then everything is satisfied and this issue can be closed.