1
0
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:
Michael Mclaughlin 2017-06-27 22:07:37 +01:00
parent 32f0a63095
commit 22ac9377c6
3 changed files with 87 additions and 60 deletions

View File

@ -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,7 +2247,7 @@ 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));
@ -2264,27 +2264,32 @@ 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;
// 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.
// x^y = 10^e, where e = y * log10(x)
@ -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,6 +2317,9 @@ P.toPower = P.pow = function (y) {
// r = x^y = exp(y*ln(x))
r = naturalExponential(y.times(naturalLogarithm(x, pr + k)), pr);
// r may be Infinity, e.g. (0.9999999999999999).pow(-1e+40)
if (r.d) {
// Truncate to the required precision plus five rounding digits.
r = finalise(r, pr + 5, 1);
@ -2328,8 +2336,9 @@ P.toPower = P.pow = function (y) {
r = finalise(r, pr + 1, 0);
}
}
}
r.s = sign;
r.s = s;
external = true;
Ctor.rounding = rm;

View File

@ -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,7 +2249,7 @@
*
*/
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));
@ -2266,42 +2266,46 @@
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;
// 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.
// x^y = 10^e, where e = y * log10(x)
// 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,6 +2319,9 @@
// r = x^y = exp(y*ln(x))
r = naturalExponential(y.times(naturalLogarithm(x, pr + k)), pr);
// r may be Infinity, e.g. (0.9999999999999999).pow(-1e+40)
if (r.d) {
// Truncate to the required precision plus five rounding digits.
r = finalise(r, pr + 5, 1);
@ -2331,8 +2338,9 @@
r = finalise(r, pr + 1, 0);
}
}
}
r.s = sign;
r.s = s;
external = true;
Ctor.rounding = rm;

View File

@ -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);
});