fable 3.1.19 → 3.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fable",
3
- "version": "3.1.19",
3
+ "version": "3.1.21",
4
4
  "description": "A service dependency injection, configuration and logging library.",
5
5
  "main": "source/Fable.js",
6
6
  "scripts": {
@@ -55,16 +55,16 @@
55
55
  "dependencies": {
56
56
  "async.eachlimit": "^0.5.2",
57
57
  "async.waterfall": "^0.5.2",
58
- "big.js": "^6.2.2",
58
+ "big.js": "^7.0.1",
59
59
  "cachetrax": "^1.0.4",
60
- "cookie": "^0.6.0",
60
+ "cookie": "^1.0.2",
61
61
  "data-arithmatic": "^1.0.7",
62
- "dayjs": "^1.11.13",
62
+ "dayjs": "^1.11.18",
63
63
  "fable-log": "^3.0.16",
64
64
  "fable-serviceproviderbase": "^3.0.15",
65
65
  "fable-settings": "^3.0.12",
66
66
  "fable-uuid": "^3.0.11",
67
- "manyfest": "^1.0.41",
67
+ "manyfest": "^1.0.42",
68
68
  "simple-get": "^4.0.1"
69
69
  }
70
70
  }
@@ -41,6 +41,16 @@
41
41
  "Address": "fable.Math.eulerPrecise"
42
42
  },
43
43
 
44
+ "log": {
45
+ "Name": "Logarithm",
46
+ "Address": "fable.Math.logPrecise"
47
+ },
48
+ "exp": {
49
+ "Name": "Eulers Number to the Power Of N",
50
+ "Address": "fable.Math.expPrecise"
51
+ },
52
+
53
+
44
54
  "sin": {
45
55
  "Name": "Sine",
46
56
  "Address": "fable.Math.sin"
@@ -26,7 +26,10 @@ class FableServiceMath extends libFableServiceBase
26
26
  // From NASA: https://apod.nasa.gov/htmltest/gifcity/e.2mil
27
27
  this.euler = '2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664';
28
28
 
29
- // this.manifest = this.fable.newManyfest();
29
+ // this.manifest = this.fable.newManyfest();
30
+ this.bigNumber = this.fable.Utility.bigNumber;
31
+
32
+ this.ln2Cache = new Map();
30
33
  }
31
34
 
32
35
  /*
@@ -39,10 +42,10 @@ class FableServiceMath extends libFableServiceBase
39
42
  roundHalfEven 2 ROUND_HALF_EVEN Rounds towards nearest neighbour. (_If equidistant, rounds towards even neighbour._)
40
43
  roundUp 3 ROUND_UP Rounds positively away from zero. (_Always round up._)
41
44
  */
42
- get roundDown() { return this.fable.Utility.bigNumber.roundDown; }
43
- get roundHalfUp() { return this.fable.Utility.bigNumber.roundHalfUp; }
44
- get roundHalfEven() { return this.fable.Utility.bigNumber.roundHalfEven; }
45
- get roundUp() { return this.fable.Utility.bigNumber.roundUp; }
45
+ get roundDown() { return this.bigNumber.roundDown; }
46
+ get roundHalfUp() { return this.bigNumber.roundHalfUp; }
47
+ get roundHalfEven() { return this.bigNumber.roundHalfEven; }
48
+ get roundUp() { return this.bigNumber.roundUp; }
46
49
 
47
50
  /**
48
51
  * Parses a precise number value.
@@ -57,7 +60,7 @@ class FableServiceMath extends libFableServiceBase
57
60
 
58
61
  try
59
62
  {
60
- tmpNumber = new this.fable.Utility.bigNumber(pValue);
63
+ tmpNumber = new this.bigNumber(pValue);
61
64
  }
62
65
  catch (pError)
63
66
  {
@@ -97,7 +100,7 @@ class FableServiceMath extends libFableServiceBase
97
100
  return '0';
98
101
  }
99
102
 
100
- let tmpLeftArbitraryValue = new this.fable.Utility.bigNumber(tmpLeftValue);
103
+ let tmpLeftArbitraryValue = new this.bigNumber(tmpLeftValue);
101
104
  let tmpResult = tmpLeftArbitraryValue.div(tmpRightValue);
102
105
  tmpResult = tmpResult.times(100);
103
106
  return tmpResult.toString();
@@ -133,7 +136,7 @@ class FableServiceMath extends libFableServiceBase
133
136
  let tmpDecimals = isNaN(pDecimals) ? 0 : parseInt(pDecimals, 10);
134
137
  let tmpRoundingMethod = (typeof (pRoundingMethod) === 'undefined') ? this.roundHalfUp : parseInt(pRoundingMethod, 10);
135
138
 
136
- let tmpArbitraryValue = new this.fable.Utility.bigNumber(tmpValue);
139
+ let tmpArbitraryValue = new this.bigNumber(tmpValue);
137
140
  let tmpResult = tmpArbitraryValue.round(tmpDecimals, tmpRoundingMethod);
138
141
  return tmpResult.toString();
139
142
  }
@@ -152,7 +155,7 @@ class FableServiceMath extends libFableServiceBase
152
155
  let tmpDecimals = isNaN(pDecimals) ? 0 : pDecimals;
153
156
  let tmpRoundingMethod = (typeof (pRoundingMethod) === 'undefined') ? this.roundHalfUp : pRoundingMethod;
154
157
 
155
- let tmpArbitraryValue = new this.fable.Utility.bigNumber(tmpValue);
158
+ let tmpArbitraryValue = new this.bigNumber(tmpValue);
156
159
  let tmpResult = tmpArbitraryValue.toFixed(tmpDecimals, tmpRoundingMethod);
157
160
 
158
161
  return tmpResult.toString();
@@ -169,7 +172,7 @@ class FableServiceMath extends libFableServiceBase
169
172
  let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
170
173
  let tmpRightValue = isNaN(pRightValue) ? 0 : pRightValue;
171
174
 
172
- let tmpLeftArbitraryValue = new this.fable.Utility.bigNumber(tmpLeftValue);
175
+ let tmpLeftArbitraryValue = new this.bigNumber(tmpLeftValue);
173
176
  let tmpResult = tmpLeftArbitraryValue.plus(tmpRightValue);
174
177
  return tmpResult.toString();
175
178
  }
@@ -186,7 +189,7 @@ class FableServiceMath extends libFableServiceBase
186
189
  let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
187
190
  let tmpRightValue = isNaN(pRightValue) ? 0 : pRightValue;
188
191
 
189
- let tmpLeftArbitraryValue = new this.fable.Utility.bigNumber(tmpLeftValue);
192
+ let tmpLeftArbitraryValue = new this.bigNumber(tmpLeftValue);
190
193
  let tmpResult = tmpLeftArbitraryValue.minus(tmpRightValue);
191
194
  return tmpResult.toString();
192
195
  }
@@ -205,7 +208,7 @@ class FableServiceMath extends libFableServiceBase
205
208
  let tmpResult;
206
209
  if (tmpRightValue == Number(pRightValue))
207
210
  {
208
- const tmpLeftArbitraryValue = new this.fable.Utility.bigNumber(tmpLeftValue);
211
+ const tmpLeftArbitraryValue = new this.bigNumber(tmpLeftValue);
209
212
  tmpResult = tmpLeftArbitraryValue.pow(tmpRightValue);
210
213
  }
211
214
  else
@@ -229,7 +232,7 @@ class FableServiceMath extends libFableServiceBase
229
232
  let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
230
233
  let tmpRightValue = isNaN(pRightValue) ? 0 : pRightValue;
231
234
 
232
- let tmpLeftArbitraryValue = new this.fable.Utility.bigNumber(tmpLeftValue);
235
+ let tmpLeftArbitraryValue = new this.bigNumber(tmpLeftValue);
233
236
  let tmpResult = tmpLeftArbitraryValue.times(tmpRightValue);
234
237
  return tmpResult.toString();
235
238
  }
@@ -246,7 +249,7 @@ class FableServiceMath extends libFableServiceBase
246
249
  let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
247
250
  let tmpRightValue = isNaN(pRightValue) ? 0 : pRightValue;
248
251
 
249
- let tmpLeftArbitraryValue = new this.fable.Utility.bigNumber(tmpLeftValue);
252
+ let tmpLeftArbitraryValue = new this.bigNumber(tmpLeftValue);
250
253
  let tmpResult = tmpLeftArbitraryValue.div(tmpRightValue);
251
254
  return tmpResult.toString();
252
255
  }
@@ -263,7 +266,7 @@ class FableServiceMath extends libFableServiceBase
263
266
  let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
264
267
  let tmpRightValue = isNaN(pRightValue) ? 0 : pRightValue;
265
268
 
266
- let tmpLeftArbitraryValue = new this.fable.Utility.bigNumber(tmpLeftValue);
269
+ let tmpLeftArbitraryValue = new this.bigNumber(tmpLeftValue);
267
270
  let tmpResult = tmpLeftArbitraryValue.mod(tmpRightValue);
268
271
  return tmpResult.toString();
269
272
  }
@@ -278,7 +281,7 @@ class FableServiceMath extends libFableServiceBase
278
281
  {
279
282
  let tmpValue = isNaN(pValue) ? 0 : pValue;
280
283
 
281
- let tmpLeftArbitraryValue = new this.fable.Utility.bigNumber(tmpValue);
284
+ let tmpLeftArbitraryValue = new this.bigNumber(tmpValue);
282
285
  let tmpResult = tmpLeftArbitraryValue.sqrt();
283
286
  return tmpResult.toString();
284
287
  }
@@ -293,7 +296,7 @@ class FableServiceMath extends libFableServiceBase
293
296
  {
294
297
  let tmpValue = isNaN(pValue) ? 0 : pValue;
295
298
 
296
- let tmpLeftArbitraryValue = new this.fable.Utility.bigNumber(tmpValue);
299
+ let tmpLeftArbitraryValue = new this.bigNumber(tmpValue);
297
300
  let tmpResult = tmpLeftArbitraryValue.abs();
298
301
  return tmpResult.toString();
299
302
  }
@@ -338,7 +341,7 @@ class FableServiceMath extends libFableServiceBase
338
341
  let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
339
342
  let tmpRightValue = isNaN(pRightValue) ? 0 : pRightValue;
340
343
 
341
- let tmpLeftArbitraryValue = new this.fable.Utility.bigNumber(tmpLeftValue);
344
+ let tmpLeftArbitraryValue = new this.bigNumber(tmpLeftValue);
342
345
  return tmpLeftArbitraryValue.cmp(tmpRightValue);
343
346
  }
344
347
 
@@ -355,7 +358,7 @@ class FableServiceMath extends libFableServiceBase
355
358
  let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
356
359
  let tmpRightValue = isNaN(pRightValue) ? 0 : pRightValue;
357
360
 
358
- let tmpLeftArbitraryValue = new this.fable.Utility.bigNumber(tmpLeftValue);
361
+ let tmpLeftArbitraryValue = new this.bigNumber(tmpLeftValue);
359
362
  const diff = tmpLeftArbitraryValue.minus(tmpRightValue).abs();
360
363
  if (diff.lte(pEpsilon))
361
364
  {
@@ -380,7 +383,7 @@ class FableServiceMath extends libFableServiceBase
380
383
  let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
381
384
  let tmpRightValue = isNaN(pRightValue) ? 0 : pRightValue;
382
385
 
383
- let tmpLeftArbitraryValue = new this.fable.Utility.bigNumber(tmpLeftValue);
386
+ let tmpLeftArbitraryValue = new this.bigNumber(tmpLeftValue);
384
387
  return tmpLeftArbitraryValue.gt(tmpRightValue);
385
388
  }
386
389
 
@@ -397,7 +400,7 @@ class FableServiceMath extends libFableServiceBase
397
400
  let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
398
401
  let tmpRightValue = isNaN(pRightValue) ? 0 : pRightValue;
399
402
 
400
- let tmpLeftArbitraryValue = new this.fable.Utility.bigNumber(tmpLeftValue);
403
+ let tmpLeftArbitraryValue = new this.bigNumber(tmpLeftValue);
401
404
  return tmpLeftArbitraryValue.gte(tmpRightValue);
402
405
  }
403
406
 
@@ -413,7 +416,7 @@ class FableServiceMath extends libFableServiceBase
413
416
  let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
414
417
  let tmpRightValue = isNaN(pRightValue) ? 0 : pRightValue;
415
418
 
416
- let tmpLeftArbitraryValue = new this.fable.Utility.bigNumber(tmpLeftValue);
419
+ let tmpLeftArbitraryValue = new this.bigNumber(tmpLeftValue);
417
420
  return tmpLeftArbitraryValue.lt(tmpRightValue);
418
421
  }
419
422
 
@@ -429,7 +432,7 @@ class FableServiceMath extends libFableServiceBase
429
432
  let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
430
433
  let tmpRightValue = isNaN(pRightValue) ? 0 : pRightValue;
431
434
 
432
- let tmpLeftArbitraryValue = new this.fable.Utility.bigNumber(tmpLeftValue);
435
+ let tmpLeftArbitraryValue = new this.bigNumber(tmpLeftValue);
433
436
  return tmpLeftArbitraryValue.lte(tmpRightValue);
434
437
  }
435
438
 
@@ -443,7 +446,7 @@ class FableServiceMath extends libFableServiceBase
443
446
  {
444
447
  let tmpDegrees = isNaN(pDegrees) ? 0 : pDegrees;
445
448
 
446
- let tmpDegreesArbitraryValue = new this.fable.Utility.bigNumber(tmpDegrees);
449
+ let tmpDegreesArbitraryValue = new this.bigNumber(tmpDegrees);
447
450
  // TODO: Const for pi in arbitrary precision?
448
451
  let tmpResult = tmpDegreesArbitraryValue.times(Math.PI).div(180);
449
452
  return tmpResult.toString();
@@ -655,7 +658,7 @@ class FableServiceMath extends libFableServiceBase
655
658
  */
656
659
  sortHistogramPrecise(pHistogram)
657
660
  {
658
- let tmpSortedHistogram = {};
661
+ let tmpSortedHistogram = {};
659
662
  let tmpKeys = Object.keys(pHistogram);
660
663
 
661
664
  tmpKeys.sort((pLeft, pRight) => { return pHistogram[pLeft] - pHistogram[pRight]; });
@@ -689,6 +692,238 @@ class FableServiceMath extends libFableServiceBase
689
692
  return tmpCleanedArray;
690
693
  }
691
694
 
695
+ /**
696
+ * Calculate the natural log of 2 to a specific precision, for use in the Taylor series.
697
+ * Cache outcome so it only runs once per precision.
698
+ * @param {number} pPrecision - The decimal precision to calculate ln(2) to.
699
+ * @returns
700
+ */
701
+ arbitraryNaturalLogOfTwo(pPrecision)
702
+ {
703
+ const tmpPrecisionKey = pPrecision | 0;
704
+ const tmpPrecision = new this.bigNumber(tmpPrecisionKey);
705
+ if (this.ln2Cache.has(tmpPrecisionKey))
706
+ {
707
+ return this.ln2Cache.get(tmpPrecisionKey);
708
+ }
709
+
710
+ const tmpTwoConstant = new this.bigNumber(2);
711
+ const y = tmpTwoConstant.minus(1).div(tmpTwoConstant.plus(1)); // 1/3
712
+ const y2 = y.mul(y);
713
+ let tmpSummation = new this.bigNumber(0);
714
+ let tmpTermination = y;
715
+ let tmpDenominator = 1;
716
+
717
+ // Use a slightly larger precision for this to prevent numeric drift for larger log requirements
718
+ const tmpEpsilon = this.powerPrecise(10, -(tmpPrecision.add(8))); // target tail < 10^-(precision+8)
719
+
720
+ for (let i = 0; i < 200000; i++)
721
+ {
722
+ tmpSummation = tmpSummation.plus(tmpTermination.div(tmpDenominator));
723
+ tmpTermination = tmpTermination.mul(y2);
724
+ tmpDenominator += 2;
725
+ if (tmpTermination.abs().div(tmpDenominator).lt(tmpEpsilon))
726
+ {
727
+ break;
728
+ }
729
+ }
730
+ const tmpNaturalLogOfTwo = tmpSummation.mul(2);
731
+ this.ln2Cache.set(tmpPrecisionKey, tmpNaturalLogOfTwo);
732
+ return tmpNaturalLogOfTwo;
733
+ }
734
+
735
+ /**
736
+ * Calculate the natural log of a number to a specific precision using arbitrary precision numbers.
737
+ * @param {number} pNumberToCompute
738
+ * @param {number} pPrecision
739
+ * @returns
740
+ */
741
+ arbitraryNaturalLog(pNumberToCompute, pPrecision)
742
+ {
743
+ let tmpNumberToCompute = new this.bigNumber(pNumberToCompute);
744
+ let tmpPrecision = new this.bigNumber(pPrecision);
745
+
746
+ if (tmpNumberToCompute.lte(0)) throw new Error('ln undefined for non-positive values.');
747
+ if (tmpNumberToCompute.eq(1)) return new this.bigNumber(0);
748
+
749
+ // Reduce x to m in ~[0.75, 1.5] by multiplying/dividing by 2
750
+ const TWO = new this.bigNumber(2);
751
+ let k = 0;
752
+ let m = tmpNumberToCompute;
753
+
754
+ const tmpUpperBounds = new this.bigNumber('1.5');
755
+ const tmpLowerBounds = new this.bigNumber('0.75');
756
+
757
+ while (m.gt(tmpUpperBounds))
758
+ {
759
+ m = m.div(TWO); k += 1;
760
+ }
761
+ while (m.lt(tmpLowerBounds))
762
+ {
763
+ m = m.mul(TWO); k -= 1;
764
+ }
765
+
766
+ // ln(m) via atanh/Taylor
767
+ const y = m.minus(1).div(m.plus(1)); // |y| < 1
768
+ const y2 = y.mul(y);
769
+
770
+ let tmpSummation = new this.bigNumber(0);
771
+ let tmpSeriesTermination = y; // y^(2j+1)
772
+ let tmpDenominator = 1;
773
+
774
+ const tmpEpsilon = this.powerPrecise(10, -(tmpPrecision.add(6))); // target tail < 10^-(precision+6)
775
+
776
+ // Iterate until next term contribution is below eps
777
+ for (let i = 0; i < 200000; i++)
778
+ {
779
+ tmpSummation = tmpSummation.plus(tmpSeriesTermination.div(tmpDenominator));
780
+ tmpSeriesTermination = tmpSeriesTermination.mul(y2);
781
+ tmpDenominator += 2;
782
+ // Stop when |tmpSeriesTermination|/tmpDenominator < eps
783
+ if (tmpSeriesTermination.abs().div(tmpDenominator).lt(tmpEpsilon))
784
+ {
785
+ break;
786
+ }
787
+ }
788
+ const tmpNaturalLog = tmpSummation.mul(2);
789
+
790
+ // ln(2) once per precision via same series with m=2 (y = 1/3 ==> for faster convergence)
791
+ const tmpPrecisionNaturalLog = this.arbitraryNaturalLogOfTwo(tmpPrecision);
792
+
793
+ return tmpNaturalLog.plus(tmpPrecisionNaturalLog.mul(k));
794
+ }
795
+
796
+ /**
797
+ * High-precision natural log using:
798
+ * - Argument reduction by powers of 2: x = m * 2^k with m ~ 1
799
+ * - atanh series: ln(m) = 2 * sum_{j>=0} y^(2j+1)/(2j+1), y=(m-1)/(m+1), |y|<1
800
+ *
801
+ * Converges rapidly when m is close to 1 with arbitrary precision numbers.
802
+ *
803
+ * @param {number} pNumberToGenerateLogarithmFor - The number to generate the logarithm for.
804
+ * @param {number} [pBase] - The base of the logarithm. Defaults to 10.
805
+ * @param {number} [pPrecision] - The precision of the result. Defaults to 9 decimal places.
806
+ * @returns {string} - The logarithm of the number to the specified base and precision.
807
+ */
808
+ logPrecise(pNumberToGenerateLogarithmFor, pBase, pPrecision)
809
+ {
810
+ let tmpBase = (typeof (pBase) === 'undefined') ? this.bigNumber(10) : this.bigNumber(pBase);
811
+ // Default precision is 9 decimal places -- matches Excel's default for LOG function
812
+ const tmpPrecision = (typeof (pPrecision) === 'undefined') ? 9 : pPrecision;
813
+ // Extra precision to avoid rounding errors since we are using a series
814
+ const tmpExtraPrecision = 8;
815
+ const tmpWorkingPrecision = tmpPrecision + tmpExtraPrecision;
816
+
817
+ // Store existing precision since this function integrates on a its own precision terms
818
+ const tmpSavedBigDecimalPrecision = this.bigNumber.DP;
819
+ const tmpSavedBigRoundingMethod = this.bigNumber.RM;
820
+
821
+ this.bigNumber.DP = tmpWorkingPrecision;
822
+ this.bigNumber.RM = 1; // round half up for the Taylor series
823
+
824
+ const N = this.bigNumber(pNumberToGenerateLogarithmFor);
825
+ const B = this.bigNumber(tmpBase);
826
+
827
+ // Run domain checks, which Excel also does
828
+ if (N.lte(0))
829
+ {
830
+ this.log.error(`Fable logPrecise Error: Number must be greater than 0; number was ${pNumberToGenerateLogarithmFor}.`);
831
+ return NaN;
832
+ }
833
+ if (B.lte(0) || B.eq(1))
834
+ {
835
+ this.log.error(`Fable logPrecise Error: Base must be greater than 0 and not equal to 1 -- base ${Base} was passed in.`);
836
+ return NaN;
837
+ }
838
+
839
+ const tmpNaturalLogOfN = this.arbitraryNaturalLog(N, tmpPrecision);
840
+ const tmpNaturalLogOfB = this.arbitraryNaturalLog(B, tmpPrecision);
841
+
842
+ const tmpResult = tmpNaturalLogOfN.div(tmpNaturalLogOfB);
843
+
844
+ // Final rounding to requested precision
845
+ let finalResult = tmpResult.toFixed(tmpPrecision);
846
+ this.bigNumber.DP = tmpSavedBigDecimalPrecision;
847
+ this.bigNumber.RM = tmpSavedBigRoundingMethod;
848
+ return finalResult;
849
+ }
850
+
851
+ expPrecise(pValue, pDecimalPrecision)
852
+ {
853
+ let tmpValue = isNaN(pValue) ? this.bigNumber(1) : this.bigNumber(pValue);
854
+
855
+ // Constants & thresholds (Excel / IEEE-754 double limits) -- this is required to match Excel's behavior
856
+ const tmpDecimalPrecision = (typeof (pDecimalPrecision) === 'undefined') ? 9 : parseInt(pDecimalPrecision, 10);
857
+ const tmpSavedBigDecimalPrecision = this.bigNumber.DP;
858
+ this.bigNumber.DP = tmpDecimalPrecision + 10; // a bit of extra precision for rounding safety (this makes it match excel)
859
+
860
+ // ln(2), min/max natural logs before double overflow/underflow
861
+ const tmpNaturalLogOfTwo = new this.bigNumber('0.693147180559945309417232121458176568'); // This is hilarious that we can compute the value above but this is what Excel uses.
862
+ const tmpNaturalLogMaxDouble = new this.bigNumber('709.782712893384'); // ln(1.7976931348623157e308)
863
+ const tmpNaturalLogMinimumValue = new this.bigNumber('-744.4400719213812'); // ln(5e-324)
864
+
865
+ // 1. Guard for Overflow / underflow behavior to match Excel
866
+ if (tmpValue.gt(tmpNaturalLogMaxDouble))
867
+ {
868
+ this.bigNumber.DP = tmpSavedBigDecimalPrecision;
869
+ return NaN; // Excel shows #NUM! when result overflows we will use NaN
870
+ }
871
+ if (tmpValue.lt(tmpNaturalLogMinimumValue))
872
+ {
873
+ this.bigNumber.DP = tmpSavedBigDecimalPrecision;
874
+ return new this.bigNumber(0); // Excel underflows to 0
875
+ }
876
+
877
+ // 2. Perform Range reduction: x = k*ln2 + r, with r small
878
+ // k = floor(x / ln2)
879
+ let k;
880
+ try
881
+ {
882
+ k = tmpValue.div(tmpNaturalLogOfTwo).round(0, 0 /* RoundDown toward -infinity */); // floor for positives & negatives
883
+ }
884
+ catch(pErrorRounding)
885
+ {
886
+ this.log.error(`Fable expPrecise Error: Rounding error during range reduction for value of ${pValue}. Error: ${pErrorRounding}`);
887
+ this.bigNumber.DP = tmpSavedBigDecimalPrecision;
888
+ return NaN;
889
+ }
890
+ const r = tmpValue.minus(k.times(tmpNaturalLogOfTwo));
891
+
892
+ // Compute exp(r) via Taylor series with Big arithmetic
893
+ // exp(r) = Summation of r^n / n!, n=0..infinity
894
+ // Sum until termination is below tolerance based on decimal precision
895
+ const tmpTolerance = new this.bigNumber(10).pow(-(tmpDecimalPrecision + 2));
896
+ let tmpTermination = new this.bigNumber(1); // r^0/0! = 1
897
+ let tmpSummation = new this.bigNumber(1);
898
+ let n = 1;
899
+
900
+ // 3. Multiply incrementally: term *= r / n
901
+ // SOOOOO close to a fractal!
902
+ while (true)
903
+ {
904
+ tmpTermination = tmpTermination.times(r).div(n);
905
+ if (tmpTermination.abs().lt(tmpTolerance)) break;
906
+ tmpSummation = tmpSummation.plus(tmpTermination);
907
+ n++;
908
+ // Hard safety cap for pathological inputs (shouldn’t be possible with step 2's range reduction):
909
+ if (n > 2000)
910
+ {
911
+ this.log.warn(`Fable expPrecise warning: Taylor series failed to converge after 2000 iterations for value of ${pValue}.`);
912
+ break;
913
+ }
914
+ }
915
+
916
+ // 4. Recompose: exp(x) = 2^k * exp(r)
917
+ const tmpTwo = new this.bigNumber(2);
918
+ const tmpAbsoluteValueOfK = k.abs().toNumber(); // k is integer; big.js pow requires a JS integer
919
+ let tmpTwoToThePowerOfAbsoluteK = tmpAbsoluteValueOfK === 0 ? new this.bigNumber(1) : tmpTwo.pow(tmpAbsoluteValueOfK);
920
+ const tmpResult = k.gte(0) ? tmpSummation.times(tmpTwoToThePowerOfAbsoluteK) : tmpSummation.div(tmpTwoToThePowerOfAbsoluteK);
921
+
922
+ // 5. Restore global decimal precision
923
+ this.bigNumber.DP = tmpSavedBigDecimalPrecision;
924
+ return tmpResult.round(tmpDecimalPrecision).toString();
925
+ }
926
+
692
927
  cleanValueObject(pValueObject)
693
928
  {
694
929
  if (typeof (pValueObject) !== 'object')
package/test/Math_test.js CHANGED
@@ -295,6 +295,80 @@ suite
295
295
  return fDone();
296
296
  }
297
297
  );
298
+
299
+ test
300
+ (
301
+ 'Logarithms',
302
+ function(fDone)
303
+ {
304
+ let testFable = new libFable();
305
+
306
+ Expect(testFable.Math.logPrecise(0)).to.be.NaN;
307
+ Expect(testFable.Math.logPrecise(1.124)).to.equal('0.050766311');
308
+ Expect(testFable.Math.logPrecise('1.124')).to.equal('0.050766311');
309
+ Expect(testFable.Math.logPrecise('1.4')).to.equal('0.146128036');
310
+ Expect(testFable.Math.logPrecise('1.874232')).to.equal('0.272823349');
311
+ Expect(testFable.Math.logPrecise('1.9')).to.equal('0.278753601');
312
+ Expect(testFable.Math.logPrecise('2.2')).to.equal('0.342422681');
313
+ Expect(testFable.Math.logPrecise('3.324')).to.equal('0.521661015');
314
+ Expect(testFable.Math.logPrecise('6.32423')).to.equal('0.801007656');
315
+ Expect(testFable.Math.logPrecise('7')).to.equal('0.845098040');
316
+ Expect(testFable.Math.logPrecise('16')).to.equal('1.204119983');
317
+ Expect(testFable.Math.logPrecise('10000')).to.equal('4.000000000');
318
+ Expect(testFable.Math.logPrecise('87')).to.equal('1.939519253');
319
+ Expect(testFable.Math.logPrecise('100')).to.equal('2.000000000');
320
+ Expect(testFable.Math.logPrecise('110')).to.equal('2.041392685');
321
+ Expect(testFable.Math.logPrecise('130')).to.equal('2.113943352');
322
+ Expect(testFable.Math.logPrecise('400')).to.equal('2.602059991');
323
+ Expect(testFable.Math.logPrecise('500')).to.equal('2.698970004');
324
+ Expect(testFable.Math.logPrecise('600')).to.equal('2.778151250');
325
+ Expect(testFable.Math.logPrecise('900')).to.equal('2.954242509');
326
+ Expect(testFable.Math.logPrecise('999')).to.equal('2.999565488');
327
+ Expect(testFable.Math.logPrecise('1000')).to.equal('3.000000000');
328
+ Expect(testFable.Math.logPrecise('1001')).to.equal('3.000434077');
329
+ Expect(testFable.Math.logPrecise('1200')).to.equal('3.079181246');
330
+ Expect(testFable.Math.logPrecise('1300')).to.equal('3.113943352');
331
+ Expect(testFable.Math.logPrecise('1999')).to.equal('3.300812794');
332
+ Expect(testFable.Math.logPrecise('2000')).to.equal('3.301029996');
333
+ Expect(testFable.Math.logPrecise('3000')).to.equal('3.477121255');
334
+ Expect(testFable.Math.logPrecise('4000')).to.equal('3.602059991');
335
+ Expect(testFable.Math.logPrecise('5000')).to.equal('3.698970004');
336
+ Expect(testFable.Math.logPrecise('100000')).to.equal('5.000000000');
337
+ Expect(testFable.Math.logPrecise('1005000901')).to.equal('9.002166451');
338
+
339
+ return fDone();
340
+ }
341
+ );
342
+
343
+ test
344
+ (
345
+ 'Eulers to an Exponent of N',
346
+ function(fDone)
347
+ {
348
+ let testFable = new libFable();
349
+
350
+ Expect(testFable.Math.expPrecise('0')).to.equal('1');
351
+ Expect(testFable.Math.expPrecise('1.124')).to.equal('3.077138172');
352
+ Expect(testFable.Math.expPrecise('1.4')).to.equal('4.055199967');
353
+ Expect(testFable.Math.expPrecise('1.874232')).to.equal('6.515813054');
354
+ Expect(testFable.Math.expPrecise('1.9')).to.equal('6.685894442');
355
+ Expect(testFable.Math.expPrecise('2.2')).to.equal('9.025013499');
356
+ Expect(testFable.Math.expPrecise('3.324')).to.equal('27.771213539');
357
+ Expect(testFable.Math.expPrecise('6.32423')).to.equal('557.928043628');
358
+ Expect(testFable.Math.expPrecise('7')).to.equal('1096.633158427');
359
+ Expect(testFable.Math.expPrecise('16')).to.equal('8886110.52050434');
360
+ Expect(testFable.Math.expPrecise('87')).to.equal('6.0760302250165910226862305668049228933571005991e+37');
361
+ Expect(testFable.Math.expPrecise('100')).to.equal('2.6881171418144006498361355299904430489510323068101727e+43');
362
+ Expect(testFable.Math.expPrecise('110')).to.equal('5.92097202763302871775566547178919934033680861763789992544e+47');
363
+ Expect(testFable.Math.expPrecise('130')).to.equal('2.87264955081656835249498360594656214322410986618852673463510287128e+56');
364
+ Expect(testFable.Math.expPrecise('400')).to.equal('5.22146968976280766823448917819312119928757838636294365087229965314955319234363035967068090474969920921229926737521823711259606341428359454445036263563446386876069161748564476869487107e+173');
365
+ Expect(testFable.Math.expPrecise('500')).to.equal('1.4035922178443322635139346187029106729839029888278346358090479055036847329857327659066066346423163346684518112330560812299308307757301544400486805796338124141628072493187592353782193348029633841832360152127536485213047179412609e+217');
366
+ Expect(testFable.Math.expPrecise('600')).to.equal('3.77302030092434336419695838674700740088354673638834622388500325660794131835628047153390042345698138347361656320402231485978980283954189824175081056524926295674148482949243810250377530201023308557571234567617672508975937688969808748216814845636042130603884174370820531865e+260');
367
+
368
+ return fDone();
369
+ }
370
+ )
371
+
298
372
  }
299
373
  );
300
374
  }