mirror of
https://github.com/MikeMcl/decimal.js.git
synced 2024-10-27 20:34:12 +00:00
Bugfix: #58 pow sometimes throws when result is Infinity
This commit is contained in:
parent
32f0a63095
commit
22ac9377c6
@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* decimal.js v7.2.2
|
||||
* decimal.js v7.2.3
|
||||
* An arbitrary-precision Decimal type for JavaScript.
|
||||
* https://github.com/MikeMcl/decimal.js
|
||||
* Copyright (c) 2017 Michael Mclaughlin <M8ch88l@gmail.com>
|
||||
@ -2247,13 +2247,13 @@ P.toOctal = function (sd, rm) {
|
||||
*
|
||||
*/
|
||||
P.toPower = P.pow = function (y) {
|
||||
var e, k, pr, r, rm, sign, yIsInt,
|
||||
var e, k, pr, r, rm, s,
|
||||
x = this,
|
||||
Ctor = x.constructor,
|
||||
yn = +(y = new Ctor(y));
|
||||
|
||||
// Either ±Infinity, NaN or ±0?
|
||||
if (!x.d || !y.d || !x.d[0] || !y.d[0]) return new Ctor(mathpow(+x, yn));
|
||||
if (!x.d || !y.d || !x.d[0] || !y.d[0]) return new Ctor(mathpow(+x, yn));
|
||||
|
||||
x = new Ctor(x);
|
||||
|
||||
@ -2264,26 +2264,31 @@ P.toPower = P.pow = function (y) {
|
||||
|
||||
if (y.eq(1)) return finalise(x, pr, rm);
|
||||
|
||||
// y exponent
|
||||
e = mathfloor(y.e / LOG_BASE);
|
||||
k = y.d.length - 1;
|
||||
yIsInt = e >= k;
|
||||
sign = x.s;
|
||||
|
||||
if (!yIsInt) {
|
||||
if (sign < 0) return new Ctor(NaN);
|
||||
|
||||
// If y is a small integer use the 'exponentiation by squaring' algorithm.
|
||||
} else if ((k = yn < 0 ? -yn : yn) <= MAX_SAFE_INTEGER) {
|
||||
if (e >= y.d.length - 1 && (k = yn < 0 ? -yn : yn) <= MAX_SAFE_INTEGER) {
|
||||
r = intPow(Ctor, x, k, pr);
|
||||
return y.s < 0 ? new Ctor(1).div(r) : finalise(r, pr, rm);
|
||||
}
|
||||
|
||||
// Result is negative if x is negative and the last digit of integer y is odd.
|
||||
sign = sign < 0 && y.d[Math.max(e, k)] & 1 ? -1 : 1;
|
||||
s = x.s;
|
||||
|
||||
if (x.eq(-1)) {
|
||||
x.s = sign;
|
||||
return x;
|
||||
// if x is negative
|
||||
if (s < 0) {
|
||||
|
||||
// if y is not an integer
|
||||
if (e < y.d.length - 1) return new Ctor(NaN);
|
||||
|
||||
// Result is positive if x is negative and the last digit of integer y is even.
|
||||
if ((y.d[e] & 1) == 0) s = 1;
|
||||
|
||||
// if x.eq(-1)
|
||||
if (x.e == 0 && x.d[0] == 1 && x.d.length == 1) {
|
||||
x.s = s;
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
// Estimate result exponent.
|
||||
@ -2295,10 +2300,10 @@ P.toPower = P.pow = function (y) {
|
||||
? mathfloor(yn * (Math.log('0.' + digitsToString(x.d)) / Math.LN10 + x.e + 1))
|
||||
: new Ctor(k + '').e;
|
||||
|
||||
// Estimate may be incorrect e.g. x: 0.999999999999999999, y: 2.29, e: 0, r.e: -1.
|
||||
// Exponent estimate may be incorrect e.g. x: 0.999999999999999999, y: 2.29, e: 0, r.e: -1.
|
||||
|
||||
// Overflow/underflow?
|
||||
if (e > Ctor.maxE + 1 || e < Ctor.minE - 1) return new Ctor(e > 0 ? sign / 0 : 0);
|
||||
if (e > Ctor.maxE + 1 || e < Ctor.minE - 1) return new Ctor(e > 0 ? s / 0 : 0);
|
||||
|
||||
external = false;
|
||||
Ctor.rounding = x.s = 1;
|
||||
@ -2312,24 +2317,28 @@ P.toPower = P.pow = function (y) {
|
||||
// r = x^y = exp(y*ln(x))
|
||||
r = naturalExponential(y.times(naturalLogarithm(x, pr + k)), pr);
|
||||
|
||||
// Truncate to the required precision plus five rounding digits.
|
||||
r = finalise(r, pr + 5, 1);
|
||||
// r may be Infinity, e.g. (0.9999999999999999).pow(-1e+40)
|
||||
if (r.d) {
|
||||
|
||||
// If the rounding digits are [49]9999 or [50]0000 increase the precision by 10 and recalculate
|
||||
// the result.
|
||||
if (checkRoundingDigits(r.d, pr, rm)) {
|
||||
e = pr + 10;
|
||||
// Truncate to the required precision plus five rounding digits.
|
||||
r = finalise(r, pr + 5, 1);
|
||||
|
||||
// Truncate to the increased precision plus five rounding digits.
|
||||
r = finalise(naturalExponential(y.times(naturalLogarithm(x, e + k)), e), e + 5, 1);
|
||||
// If the rounding digits are [49]9999 or [50]0000 increase the precision by 10 and recalculate
|
||||
// the result.
|
||||
if (checkRoundingDigits(r.d, pr, rm)) {
|
||||
e = pr + 10;
|
||||
|
||||
// Check for 14 nines from the 2nd rounding digit (the first rounding digit may be 4 or 9).
|
||||
if (+digitsToString(r.d).slice(pr + 1, pr + 15) + 1 == 1e14) {
|
||||
r = finalise(r, pr + 1, 0);
|
||||
// Truncate to the increased precision plus five rounding digits.
|
||||
r = finalise(naturalExponential(y.times(naturalLogarithm(x, e + k)), e), e + 5, 1);
|
||||
|
||||
// Check for 14 nines from the 2nd rounding digit (the first rounding digit may be 4 or 9).
|
||||
if (+digitsToString(r.d).slice(pr + 1, pr + 15) + 1 == 1e14) {
|
||||
r = finalise(r, pr + 1, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.s = sign;
|
||||
r.s = s;
|
||||
external = true;
|
||||
Ctor.rounding = rm;
|
||||
|
||||
|
70
decimal.js
70
decimal.js
@ -1,10 +1,10 @@
|
||||
/*! decimal.js v7.2.2 https://github.com/MikeMcl/decimal.js/LICENCE */
|
||||
/*! decimal.js v7.2.3 https://github.com/MikeMcl/decimal.js/LICENCE */
|
||||
;(function (globalScope) {
|
||||
'use strict';
|
||||
|
||||
|
||||
/*
|
||||
* decimal.js v7.2.2
|
||||
* decimal.js v7.2.3
|
||||
* An arbitrary-precision Decimal type for JavaScript.
|
||||
* https://github.com/MikeMcl/decimal.js
|
||||
* Copyright (c) 2017 Michael Mclaughlin <M8ch88l@gmail.com>
|
||||
@ -2249,13 +2249,13 @@
|
||||
*
|
||||
*/
|
||||
P.toPower = P.pow = function (y) {
|
||||
var e, k, pr, r, rm, sign, yIsInt,
|
||||
var e, k, pr, r, rm, s,
|
||||
x = this,
|
||||
Ctor = x.constructor,
|
||||
yn = +(y = new Ctor(y));
|
||||
|
||||
// Either ±Infinity, NaN or ±0?
|
||||
if (!x.d || !y.d || !x.d[0] || !y.d[0]) return new Ctor(mathpow(+x, yn));
|
||||
if (!x.d || !y.d || !x.d[0] || !y.d[0]) return new Ctor(mathpow(+x, yn));
|
||||
|
||||
x = new Ctor(x);
|
||||
|
||||
@ -2266,26 +2266,31 @@
|
||||
|
||||
if (y.eq(1)) return finalise(x, pr, rm);
|
||||
|
||||
// y exponent
|
||||
e = mathfloor(y.e / LOG_BASE);
|
||||
k = y.d.length - 1;
|
||||
yIsInt = e >= k;
|
||||
sign = x.s;
|
||||
|
||||
if (!yIsInt) {
|
||||
if (sign < 0) return new Ctor(NaN);
|
||||
|
||||
// If y is a small integer use the 'exponentiation by squaring' algorithm.
|
||||
} else if ((k = yn < 0 ? -yn : yn) <= MAX_SAFE_INTEGER) {
|
||||
if (e >= y.d.length - 1 && (k = yn < 0 ? -yn : yn) <= MAX_SAFE_INTEGER) {
|
||||
r = intPow(Ctor, x, k, pr);
|
||||
return y.s < 0 ? new Ctor(1).div(r) : finalise(r, pr, rm);
|
||||
}
|
||||
|
||||
// Result is negative if x is negative and the last digit of integer y is odd.
|
||||
sign = sign < 0 && y.d[Math.max(e, k)] & 1 ? -1 : 1;
|
||||
s = x.s;
|
||||
|
||||
if (x.eq(-1)) {
|
||||
x.s = sign;
|
||||
return x;
|
||||
// if x is negative
|
||||
if (s < 0) {
|
||||
|
||||
// if y is not an integer
|
||||
if (e < y.d.length - 1) return new Ctor(NaN);
|
||||
|
||||
// Result is positive if x is negative and the last digit of integer y is even.
|
||||
if ((y.d[e] & 1) == 0) s = 1;
|
||||
|
||||
// if x.eq(-1)
|
||||
if (x.e == 0 && x.d[0] == 1 && x.d.length == 1) {
|
||||
x.s = s;
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
// Estimate result exponent.
|
||||
@ -2293,15 +2298,14 @@
|
||||
// log10(x) = log10(x_significand) + x_exponent
|
||||
// log10(x_significand) = ln(x_significand) / ln(10)
|
||||
k = mathpow(+x, yn);
|
||||
|
||||
e = k == 0 || !isFinite(k)
|
||||
? mathfloor(yn * (Math.log('0.' + digitsToString(x.d)) / Math.LN10 + x.e + 1))
|
||||
: new Ctor(k + '').e;
|
||||
|
||||
// Estimate may be incorrect e.g. x: 0.999999999999999999, y: 2.29, e: 0, r.e: -1.
|
||||
// Exponent estimate may be incorrect e.g. x: 0.999999999999999999, y: 2.29, e: 0, r.e: -1.
|
||||
|
||||
// Overflow/underflow?
|
||||
if (e > Ctor.maxE + 1 || e < Ctor.minE - 1) return new Ctor(e > 0 ? sign / 0 : 0);
|
||||
if (e > Ctor.maxE + 1 || e < Ctor.minE - 1) return new Ctor(e > 0 ? s / 0 : 0);
|
||||
|
||||
external = false;
|
||||
Ctor.rounding = x.s = 1;
|
||||
@ -2315,24 +2319,28 @@
|
||||
// r = x^y = exp(y*ln(x))
|
||||
r = naturalExponential(y.times(naturalLogarithm(x, pr + k)), pr);
|
||||
|
||||
// Truncate to the required precision plus five rounding digits.
|
||||
r = finalise(r, pr + 5, 1);
|
||||
// r may be Infinity, e.g. (0.9999999999999999).pow(-1e+40)
|
||||
if (r.d) {
|
||||
|
||||
// If the rounding digits are [49]9999 or [50]0000 increase the precision by 10 and recalculate
|
||||
// the result.
|
||||
if (checkRoundingDigits(r.d, pr, rm)) {
|
||||
e = pr + 10;
|
||||
// Truncate to the required precision plus five rounding digits.
|
||||
r = finalise(r, pr + 5, 1);
|
||||
|
||||
// Truncate to the increased precision plus five rounding digits.
|
||||
r = finalise(naturalExponential(y.times(naturalLogarithm(x, e + k)), e), e + 5, 1);
|
||||
// If the rounding digits are [49]9999 or [50]0000 increase the precision by 10 and recalculate
|
||||
// the result.
|
||||
if (checkRoundingDigits(r.d, pr, rm)) {
|
||||
e = pr + 10;
|
||||
|
||||
// Check for 14 nines from the 2nd rounding digit (the first rounding digit may be 4 or 9).
|
||||
if (+digitsToString(r.d).slice(pr + 1, pr + 15) + 1 == 1e14) {
|
||||
r = finalise(r, pr + 1, 0);
|
||||
// Truncate to the increased precision plus five rounding digits.
|
||||
r = finalise(naturalExponential(y.times(naturalLogarithm(x, e + k)), e), e + 5, 1);
|
||||
|
||||
// Check for 14 nines from the 2nd rounding digit (the first rounding digit may be 4 or 9).
|
||||
if (+digitsToString(r.d).slice(pr + 1, pr + 15) + 1 == 1e14) {
|
||||
r = finalise(r, pr + 1, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.s = sign;
|
||||
r.s = s;
|
||||
external = true;
|
||||
Ctor.rounding = rm;
|
||||
|
||||
|
@ -109,6 +109,11 @@ T('pow', function () {
|
||||
t('8.97', '-1', '0.111482720178', 12, 3);
|
||||
t('61766796871807246.3278075', '-1', '0.00000000000000001618993', 7, 0);
|
||||
|
||||
t('-1', '101', '-1', 100, 1);
|
||||
t('-1', '9999999999999999999999999999999999999999999999999999999999999999999999999', '-1', 100, 1);
|
||||
t('-1', '1e307', '1', 100, 1);
|
||||
t('-1', '1e309', '1', 100, 1);
|
||||
|
||||
Decimal.toExpNeg = Decimal.toExpPos = 0;
|
||||
|
||||
t('9.9999999999999', '2220.75', '5.623413251778e+2220', 13, 1);
|
||||
@ -126,4 +131,9 @@ T('pow', function () {
|
||||
t('908948247.896330216349750387912923575076135766138', '11.38907521122213262858256836', '1.0702278292293091784680297675223031e+102', 35, 3);
|
||||
t('4.485925762349120387154391E+47', '1677945.16766265206929939', '8.53959030215133943e+79957194', 18, 5);
|
||||
t('2.8448989811706207675566E+89', '2.368592228588521845032068137267440272102614', '7.58940197453762187722508511706932e+211', 33, 5);
|
||||
|
||||
t('0.9999999999999999', '-1e+30', '1.530863912e+43429448190325', 10, 1);
|
||||
t('0.9999999999999999999999999999999999999999999999999', '-1e+32', '1.00000000000000001000000000000000005e+0', 36, 1);
|
||||
t('0.9999999999999999', '-1e+50', 'Infinity', 40, 1);
|
||||
t('0.9999999999999999999999999999999899999999999999994403269002375809806554775739676251993670310626872684', '-1.49181945463118148622657269735650603014891811120124843379694396257337810020127409048127397077199569e+271', 'Infinity', 100, 1);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user