From fd668fea70a41cf8e2493aee7125861c84738463 Mon Sep 17 00:00:00 2001 From: Michael Mclaughlin Date: Sat, 9 Dec 2017 22:05:28 +0000 Subject: [PATCH] Add Decimal.isDecimal and config reset --- decimal.d.ts | 3 ++ decimal.js | 33 ++++++++++++++++----- decimal.mjs | 45 +++++++++++++++++++--------- doc/API.html | 59 +++++++++++++++++++++++++++++-------- test/modules/clone.js | 35 ++++++++++++++++++++++ test/modules/config.js | 31 +++++++++++++++++++ test/modules/isFiniteEtc.js | 20 ++++++++++++- 7 files changed, 191 insertions(+), 35 deletions(-) diff --git a/decimal.d.ts b/decimal.d.ts index d072ec7..8bee33a 100644 --- a/decimal.d.ts +++ b/decimal.d.ts @@ -76,6 +76,7 @@ interface DecimalConfig { // Requires `toFormat` . format?: DecimalFormat; + defaults?: boolean; } // Requires `toFormat`. @@ -92,6 +93,7 @@ export declare class Decimal { readonly d: number[]; readonly e: number; readonly s: number; + private readonly name: string; constructor(n: DecimalValue); @@ -285,6 +287,7 @@ export declare class Decimal { static exp(n: DecimalValue): Decimal static floor(n: DecimalValue): Decimal static hypot(...n: DecimalValue[]): Decimal + static isDecimal(object: any): boolean static ln(n: DecimalValue): Decimal static log(n: DecimalValue, base?: DecimalValue): Decimal static log2(n: DecimalValue): Decimal diff --git a/decimal.js b/decimal.js index d1f27d8..addde56 100644 --- a/decimal.js +++ b/decimal.js @@ -34,7 +34,7 @@ // The initial configuration properties of the Decimal constructor. - Decimal = { + DEFAULTS = { // These values must be integers within the stated ranges (inclusive). // Most of these values can be changed at run-time using the `Decimal.config` method. @@ -99,7 +99,7 @@ // ----------------------------------- END OF EDITABLE DEFAULTS ------------------------------- // - inexact, noConflict, quadrant, + Decimal, inexact, noConflict, quadrant, external = true, decimalError = '[DecimalError] ', @@ -123,7 +123,7 @@ PI_PRECISION = PI.length - 1, // Decimal.prototype object - P = {}; + P = { name: '[object Decimal]' }; // Decimal prototype methods @@ -4160,6 +4160,7 @@ * minE {number} * modulo {number} * crypto {boolean|number} + * defaults {true} * * E.g. Decimal.config({ precision: 20, rounding: 4 }) * @@ -4167,6 +4168,7 @@ function config(obj) { if (!obj || typeof obj !== 'object') throw Error(decimalError + 'Object expected'); var i, p, v, + useDefaults = obj.defaults === true, ps = [ 'precision', 1, MAX_DIGITS, 'rounding', 0, 8, @@ -4178,13 +4180,15 @@ ]; for (i = 0; i < ps.length; i += 3) { - if ((v = obj[p = ps[i]]) !== void 0) { + if (p = ps[i], useDefaults) this[p] = DEFAULTS[p]; + if ((v = obj[p]) !== void 0) { if (mathfloor(v) === v && v >= ps[i + 1] && v <= ps[i + 2]) this[p] = v; else throw Error(invalidArgument + p + ': ' + v); } } - if ((v = obj[p = 'crypto']) !== void 0) { + if (p = 'crypto', useDefaults) this[p] = DEFAULTS[p]; + if ((v = obj[p]) !== void 0) { if (v === true || v === false || v === 0 || v === 1) { if (v) { if (typeof crypto != 'undefined' && crypto && @@ -4327,6 +4331,7 @@ Decimal.config = Decimal.set = config; Decimal.clone = clone; + Decimal.isDecimal = isDecimalInstance; Decimal.abs = abs; Decimal.acos = acos; @@ -4367,8 +4372,10 @@ if (obj === void 0) obj = {}; if (obj) { - ps = ['precision', 'rounding', 'toExpNeg', 'toExpPos', 'maxE', 'minE', 'modulo', 'crypto']; - for (i = 0; i < ps.length;) if (!obj.hasOwnProperty(p = ps[i++])) obj[p] = this[p]; + if (obj.defaults !== true) { + ps = ['precision', 'rounding', 'toExpNeg', 'toExpPos', 'maxE', 'minE', 'modulo', 'crypto']; + for (i = 0; i < ps.length;) if (!obj.hasOwnProperty(p = ps[i++])) obj[p] = this[p]; + } } Decimal.config(obj); @@ -4445,6 +4452,16 @@ } + /* + * Return true if object is a Decimal instance (where Decimal is any Decimal constructor), + * otherwise return false. + * + */ + function isDecimalInstance(obj) { + return obj instanceof Decimal || obj && obj.name === '[object Decimal]' || false; + } + + /* * Return a new Decimal whose value is the natural logarithm of `x`, rounded to `precision` * significant digits using rounding mode `rounding`. @@ -4775,7 +4792,7 @@ // Create and configure initial Decimal constructor. - Decimal = clone(Decimal); + Decimal = clone(DEFAULTS); Decimal['default'] = Decimal.Decimal = Decimal; diff --git a/decimal.mjs b/decimal.mjs index 39e0cd1..595b3cb 100644 --- a/decimal.mjs +++ b/decimal.mjs @@ -25,14 +25,14 @@ var EXP_LIMIT = 9e15, // 0 to 9e15 NUMERALS = '0123456789abcdef', // The natural logarithm of 10 (1025 digits). - ln10 = '2.3025850929940456840179914546843642076011014886287729760333279009675726096773524802359972050895982983419677840422862486334095254650828067566662873690987816894829072083255546808437998948262331985283935053089653777326288461633662222876982198867465436674744042432743651550489343149393914796194044002221051017141748003688084012647080685567743216228355220114804663715659121373450747856947683463616792101806445070648000277502684916746550586856935673420670581136429224554405758925724208241314695689016758940256776311356919292033376587141660230105703089634572075440370847469940168269282808481184289314848524948644871927809676271275775397027668605952496716674183485704422507197965004714951050492214776567636938662976979522110718264549734772662425709429322582798502585509785265383207606726317164309505995087807523710333101197857547331541421808427543863591778117054309827482385045648019095610299291824318237525357709750539565187697510374970888692180205189339507238539205144634197265287286965110862571492198849978748873771345686209167058', + LN10 = '2.3025850929940456840179914546843642076011014886287729760333279009675726096773524802359972050895982983419677840422862486334095254650828067566662873690987816894829072083255546808437998948262331985283935053089653777326288461633662222876982198867465436674744042432743651550489343149393914796194044002221051017141748003688084012647080685567743216228355220114804663715659121373450747856947683463616792101806445070648000277502684916746550586856935673420670581136429224554405758925724208241314695689016758940256776311356919292033376587141660230105703089634572075440370847469940168269282808481184289314848524948644871927809676271275775397027668605952496716674183485704422507197965004714951050492214776567636938662976979522110718264549734772662425709429322582798502585509785265383207606726317164309505995087807523710333101197857547331541421808427543863591778117054309827482385045648019095610299291824318237525357709750539565187697510374970888692180205189339507238539205144634197265287286965110862571492198849978748873771345686209167058', // Pi (1025 digits). - pi = '3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632789', + PI = '3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632789', // The initial configuration properties of the Decimal constructor. - defaults = { + DEFAULTS = { // These values must be integers within the stated ranges (inclusive). // Most of these values can be changed at run-time using the `Decimal.config` method. @@ -97,7 +97,7 @@ var EXP_LIMIT = 9e15, // 0 to 9e15 // ----------------------------------- END OF EDITABLE DEFAULTS ------------------------------- // - Decimal, LN10, PI, inexact, quadrant, + Decimal, inexact, quadrant, external = true, decimalError = '[DecimalError] ', @@ -117,11 +117,11 @@ var EXP_LIMIT = 9e15, // 0 to 9e15 LOG_BASE = 7, MAX_SAFE_INTEGER = 9007199254740991, - LN10_PRECISION = ln10.length - 1, - PI_PRECISION = pi.length - 1, + LN10_PRECISION = LN10.length - 1, + PI_PRECISION = PI.length - 1, // Decimal.prototype object - P = {}; + P = { name: '[object Decimal]' }; // Decimal prototype methods @@ -4158,6 +4158,7 @@ function ceil(x) { * minE {number} * modulo {number} * crypto {boolean|number} + * defaults {true} * * E.g. Decimal.config({ precision: 20, rounding: 4 }) * @@ -4165,6 +4166,7 @@ function ceil(x) { function config(obj) { if (!obj || typeof obj !== 'object') throw Error(decimalError + 'Object expected'); var i, p, v, + useDefaults = obj.defaults === true, ps = [ 'precision', 1, MAX_DIGITS, 'rounding', 0, 8, @@ -4176,13 +4178,15 @@ function config(obj) { ]; for (i = 0; i < ps.length; i += 3) { - if ((v = obj[p = ps[i]]) !== void 0) { + if (p = ps[i], useDefaults) this[p] = DEFAULTS[p]; + if ((v = obj[p]) !== void 0) { if (mathfloor(v) === v && v >= ps[i + 1] && v <= ps[i + 2]) this[p] = v; else throw Error(invalidArgument + p + ': ' + v); } } - if ((v = obj[p = 'crypto']) !== void 0) { + if (p = 'crypto', useDefaults) this[p] = DEFAULTS[p]; + if ((v = obj[p]) !== void 0) { if (v === true || v === false || v === 0 || v === 1) { if (v) { if (typeof crypto != 'undefined' && crypto && @@ -4325,6 +4329,7 @@ function clone(obj) { Decimal.config = Decimal.set = config; Decimal.clone = clone; + Decimal.isDecimal = isDecimalInstance; Decimal.abs = abs; Decimal.acos = acos; @@ -4365,8 +4370,10 @@ function clone(obj) { if (obj === void 0) obj = {}; if (obj) { - ps = ['precision', 'rounding', 'toExpNeg', 'toExpPos', 'maxE', 'minE', 'modulo', 'crypto']; - for (i = 0; i < ps.length;) if (!obj.hasOwnProperty(p = ps[i++])) obj[p] = this[p]; + if (obj.defaults !== true) { + ps = ['precision', 'rounding', 'toExpNeg', 'toExpPos', 'maxE', 'minE', 'modulo', 'crypto']; + for (i = 0; i < ps.length;) if (!obj.hasOwnProperty(p = ps[i++])) obj[p] = this[p]; + } } Decimal.config(obj); @@ -4443,6 +4450,16 @@ function hypot() { } +/* + * Return true if object is a Decimal instance (where Decimal is any Decimal constructor), + * otherwise return false. + * + */ +function isDecimalInstance(obj) { + return obj instanceof Decimal || obj && obj.name === '[object Decimal]' || false; +} + + /* * Return a new Decimal whose value is the natural logarithm of `x`, rounded to `precision` * significant digits using rounding mode `rounding`. @@ -4773,12 +4790,12 @@ function trunc(x) { // Create and configure initial Decimal constructor. -Decimal = clone(defaults); +Decimal = clone(DEFAULTS); Decimal['default'] = Decimal.Decimal = Decimal; // Create the internal constants from their string values. -LN10 = new Decimal(ln10); -PI = new Decimal(pi); +LN10 = new Decimal(LN10); +PI = new Decimal(PI); export default Decimal; diff --git a/doc/API.html b/doc/API.html index c9f12ef..ab44236 100644 --- a/doc/API.html +++ b/doc/API.html @@ -83,6 +83,7 @@ li span{float:right;margin-right:10px;color:#c0c0c0}
  • exp
  • floor
  • hypot
  • +
  • isDecimal
  • ln
  • log
  • log2
  • @@ -195,9 +196,9 @@ li span{float:right;margin-right:10px;color:#c0c0c0} Properties Zero, NaN & Infinity @@ -446,24 +447,32 @@ a.equals(b) // true settings as this Decimal constructor if object is omitted.

    Decimal.set({ precision: 5 })
    -D9 = Decimal.clone({ precision: 9 })
    +Decimal9 = Decimal.clone({ precision: 9 })
     
     a = new Decimal(1)
    -b = new D9(1)
    +b = new Decimal9(1)
     
     a.div(3)                           // 0.33333
     b.div(3)                           // 0.333333333
     
    -// D9 = Decimal.clone({ precision: 9 }) is equivalent to:
    -D9 = Decimal.clone()
    -D9.set({ precision: 9 })
    +// Decimal9 = Decimal.clone({ precision: 9 }) is equivalent to: +Decimal9 = Decimal.clone() +Decimal9.set({ precision: 9 }) +

    + If object has a 'defaults' property with value true + then the new constructor will use the default configuration. +

    +
    +D1 = Decimal.clone({ defaults: true })
    +
    +// Use the defaults except for precision
    +D2 = Decimal.clone({ defaults: true, precision: 50 })

    It is not inefficient in terms of memory usage to use multiple Decimal constructors as functions are shared between them.

    -
    cos.cos(x) ⇒ Decimal

    x: number|string|Decimal

    See cosine.

    @@ -537,6 +546,22 @@ a.equals(b) // true +
    + isDecimal.isDecimal(object) ⇒ boolean +
    +

    object: any

    +

    + Returns true if object is a Decimal instance (where Decimal is any + Decimal constructor), or false if it is not. +

    +
    a = new Decimal(1)
    +b = {}
    +a instanceof Decimal           // true
    +Decimal.isDecimal(a)           // true
    +Decimal.isDecimal(b)           // false
    + + +
    log.log(x [, base]) ⇒ Decimal

    x: number|string|Decimal
    @@ -720,6 +745,10 @@ a.equals(b) // true The values of the configuration object properties are checked for validity and then stored as equivalently-named properties of this Decimal constructor.

    +

    + If object has a 'defaults' property with value true + then any unspecified properties will be reset to their default values. +

    Throws on an invalid object or configuration property value.

     // Defaults
    @@ -732,7 +761,13 @@ Decimal.set({
         minE: -9e15,
         modulo: 1,
         crypto: false
    -})
    +}) + +// Reset all properties to their default values +Decimal.set({ defaults: true }) + +// Set precision to 50 and all other properties to their default values +Decimal.set({ precision: 50, defaults: true })

    The properties of a Decimal constructor can also be set by direct assignment, but that will by-pass the validity checking that this method performs - this is not a problem if the user @@ -2478,9 +2513,9 @@ x.valueOf() // '-0' -1, 1, or NaN -

    The properties are best considered to be read-only.

    +

    All the properties are best considered to be read-only.

    - As with JavaScript numbers, the original exponent and fractional trailing zeros of a number + As with JavaScript numbers, the original exponent and fractional trailing zeros of a value are not preserved.

    diff --git a/test/modules/clone.js b/test/modules/clone.js
    index 5e7a457..fd76874 100644
    --- a/test/modules/clone.js
    +++ b/test/modules/clone.js
    @@ -105,5 +105,40 @@ T('clone', function () {
       t(new D8(1).constructor !== new D9(1).constructor);
     
       T.assertException(function () { Decimal.clone(null) }, "Decimal.clone(null)");
    +
    +  // defaults: true
    +
    +  Decimal.config({
    +    precision: 100,
    +    rounding: 2,
    +    toExpNeg: -100,
    +    toExpPos: 200,
    +    defaults: true
    +  });
    +
    +  t(Decimal.precision === 100);
    +  t(Decimal.rounding === 2);
    +  t(Decimal.toExpNeg === -100);
    +  t(Decimal.toExpPos === 200);
    +  t(Decimal.defaults === undefined);
    +
    +  D1 = Decimal.clone({ defaults: true });
    +
    +  t(D1.precision === 20);
    +  t(D1.rounding === 4);
    +  t(D1.toExpNeg === -7);
    +  t(D1.toExpPos === 21);
    +  t(D1.defaults === undefined);
    +
    +  D2 = Decimal.clone({ defaults: true, rounding: 5 });
    +
    +  t(D2.precision === 20);
    +  t(D2.rounding === 5);
    +  t(D2.toExpNeg === -7);
    +  t(D2.toExpPos === 21);
    +
    +  D3 = Decimal.clone({ defaults: false });
    +
    +  t(D3.rounding === 2);
     });
     
    diff --git a/test/modules/config.js b/test/modules/config.js
    index 9a3596d..08e1ea5 100644
    --- a/test/modules/config.js
    +++ b/test/modules/config.js
    @@ -338,5 +338,36 @@ T('config', function () {
     
       t(9, {modulo: void 0});
     
    +  // defaults
    +
    +  t = function (actual) {
    +    T.assert(actual);
    +  }
    +
    +  Decimal.config({
    +    precision: 100,
    +    rounding: 2,
    +    toExpNeg: -100,
    +    toExpPos: 200,
    +  });
    +
    +  t(Decimal.precision === 100);
    +
    +  Decimal.config({ defaults: true });
    +
    +  t(Decimal.precision === 20);
    +  t(Decimal.rounding === 4);
    +  t(Decimal.toExpNeg === -7);
    +  t(Decimal.toExpPos === 21);
    +  t(Decimal.defaults === undefined);
    +
    +  Decimal.rounding = 3;
    +
    +  Decimal.config({ precision: 50, defaults: true });
    +
    +  t(Decimal.precision === 50);
    +  t(Decimal.rounding === 4);
    +
    +  // Decimal.set is an alias for Decimal.config
       T.assertEqual(Decimal.set, Decimal.config);
     });
    diff --git a/test/modules/isFiniteEtc.js b/test/modules/isFiniteEtc.js
    index 04b0b73..6e3c442 100644
    --- a/test/modules/isFiniteEtc.js
    +++ b/test/modules/isFiniteEtc.js
    @@ -1,6 +1,6 @@
     if (typeof T === 'undefined') require('../setup');
     
    -T('isFinite, isInteger, isNaN, isNegative, isZero', function () {
    +T('isFinite, isInteger, isNaN, isNegative, isZero, isDecimal', function () {
     
       function t(actual) {
         T.assert(actual);
    @@ -257,4 +257,22 @@ T('isFinite, isInteger, isNaN, isNegative, isZero', function () {
       t(!new Decimal('0.999999999999999999999').isInteger());
       t(new Decimal('4e4').isInteger());
       t(new Decimal('-4e4').isInteger());
    +
    +  // Decimal.isDecimal
    +
    +  t(Decimal.isDecimal(new Decimal(1)));
    +  t(Decimal.isDecimal(new Decimal('-2.3')));
    +  t(Decimal.isDecimal(new Decimal(NaN)));
    +  t(Decimal.isDecimal(new Decimal('Infinity')));
    +
    +  t(!Decimal.isDecimal());
    +  t(!Decimal.isDecimal(0));
    +  t(!Decimal.isDecimal(1));
    +  t(!Decimal.isDecimal('-2.3'));
    +  t(!Decimal.isDecimal(NaN));
    +  t(!Decimal.isDecimal(Infinity));
    +  t(!Decimal.isDecimal(undefined));
    +  t(!Decimal.isDecimal({}));
    +  t(!Decimal.isDecimal({isDecimal: true}));
    +  t(!Decimal.isDecimal(new Number(4)));
     });