Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@yaffle/bigdecimal

Package Overview
Dependencies
Maintainers
1
Versions
36
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@yaffle/bigdecimal - npm Package Compare versions

Comparing version 1.0.36 to 2.0.0

BigDecimalMath.js

8

BigDecimal.d.ts

@@ -10,5 +10,3 @@ type RoundingMode = "floor" | "down" | "ceil" | "up" | "half-even" | "half-up" | "half-down";

declare class BigDecimal {
static BigDecimal(value: bigint | string | number | BigDecimal): BigDecimal;
static toBigInt(a: BigDecimal): bigint;
static toNumber(a: BigDecimal): number;
static BigDecimal(value: string | number | bigint | BigDecimal): BigDecimal;
static unaryMinus(a: BigDecimal): BigDecimal;

@@ -20,5 +18,3 @@ static add(a: BigDecimal, b: BigDecimal, rounding?: Rounding): BigDecimal;

static lessThan(a: BigDecimal, b: BigDecimal): boolean;
static greaterThan(a: BigDecimal, b: BigDecimal): boolean;
static equal(a: BigDecimal, b: BigDecimal): boolean;
static cmp(a: BigDecimal, b: BigDecimal): boolean;
static round(a: BigDecimal, rounding: Rounding): BigDecimal;

@@ -25,0 +21,0 @@

@@ -1,2 +0,2 @@

/*jslint bigint: true, vars: true, indent: 2*/
/*jslint bigint: true, vars: true, indent: 2, esversion:11*/

@@ -8,7 +8,6 @@ // https://github.com/tc39/proposal-decimal

// Usage:
// BigDecimal.BigDecimal(bigint)
// BigDecimal.BigDecimal(string)
// BigDecimal.BigDecimal(number) (only integers)
// BigDecimal.toBigInt(a) (not in the spec)
// BigDecimal.toNumber(a) (not in the spec, only integers)
// BigDecimal(string)
// BigDecimal(number)
// BigDecimal(bigint)
// BigDecimal.unaryMinus(a)

@@ -19,6 +18,5 @@ // BigDecimal.add(a, b[, rounding])

// BigDecimal.divide(a, b, rounding)
// BigDecimal.lessThan(a, b)
// BigDecimal.greaterThan(a, b)
// BigDecimal.equal(a, b)
// BigDecimal.cmp(a, b)
// BigDecimal.round(a, rounding)
// a.toString()

@@ -28,14 +26,3 @@ // a.toFixed(fractionDigits[, roundingMode = "half-up"])

// a.toExponential(fractionDigits[, roundingMode = "half-up"])
// Math: (not in the spec)
// BigDecimal.log(a, rounding)
// BigDecimal.exp(a, rounding)
// BigDecimal.sin(a, rounding)
// BigDecimal.cos(a, rounding)
// BigDecimal.atan(a, rounding)
// BigDecimal.sqrt(a, rounding)
// "simple" Math functions:
// BigDecimal.abs(a)
// BigDecimal.sign(a)
// BigDecimal.max(a, b)
// BigDecimal.min(a, b)
// (!) Note: consider to use only "half-even" rounding mode and rounding to a maximum number of significant digits for floating-point arithmetic,

@@ -45,5 +32,4 @@ // or only "floor" rounding to a maximum number of fraction digits for fixed-point arithmetic.

const factory = function (BASE, format = null) {
const factory = function (BASE) {
const BIGINT_BASE = BigInt(BASE);

@@ -54,8 +40,11 @@ const BASE_LOG2 = Math.log2(BASE);

const parseRegex = /^\s*([+\-])?(\d+)?\.?(\d+)?(?:e([+\-]?\d+))?\s*$/;
const defaultRounding = format === 'decimal128' ? {maximumFractionDigits: 6176, maximumSignificantDigits: 34, maximumExponent: 6144, roundingMode: 'half-even'} : null;
function BigDecimal(significand, exponent) {
this.significand = significand;
this.exponent = exponent;
function getExponent(number) {
const e = Math.floor(Math.log(Math.abs(number)) / Math.log(2)) - 1;
return Math.abs(number) / 2**e >= 2 ? e + 1 : e;
}
BigDecimal.BigFloat = BigDecimal.BigDecimal = function (value) {
function convert(value) {
if (value instanceof BigDecimal) {

@@ -68,2 +57,16 @@ return value;

}
if (format != null) {
if (value === 'Infinity') {
return create(1n, SPECIAL_EXPONENT);
}
if (value === '-Infinity') {
return create(-1n, SPECIAL_EXPONENT);
}
if (value === 'NaN') {
return create(0n, SPECIAL_EXPONENT);
}
if (value === '-0') {
return create(-1n, -SPECIAL_EXPONENT);
}
}
const match = parseRegex.exec(value);

@@ -73,4 +76,13 @@ if (match == null) {

}
const exponent = Number(match[4] || "0");
return create(BigInt((match[1] || "") + (match[2] || "") + (match[3] || "")), diff(Math.abs(exponent) < Number.MAX_SAFE_INTEGER ? exponent : BigInt(match[4] || "0"), (match[3] || "").length));
const sign = (match[1] || "");
const integer = (match[2] || "");
const fraction = (match[3] || "");
const exponent = (match[4] || "0");
let result = round(create(BigInt(sign + integer + fraction), diff(Math.abs(exponent) < Number.MAX_SAFE_INTEGER ? exponent : BigInt(match[4] || "0"), (match[3] || "").length)), null);
if (format != null) {
if (sign === "-" && result.significand === 0n) {
result = BigDecimal.unaryMinus(result);
}
}
return result;
}

@@ -85,3 +97,20 @@ if (typeof value === "number" && Math.floor(value) !== value) {

}
if (format != null) {
const e = getExponent(value);
const f = value / 2**e;
const significand = f * (Number.MAX_SAFE_INTEGER + 1) / 2;
const exponent = e - (NumberSafeBits - 1);
if (exponent >= 0) {
return BigDecimal.multiply(create(BigInt(significand), 0), create(BigInt(2)**BigInt(exponent), 0));
} else if (exponent < 0) {
return BigDecimal.divide(create(BigInt(significand), 0), create(BigInt(2)**BigInt(-exponent), 0));
}
}
}
if (value === 0 && 1 / value < 0) {
if (format != null) {
return create(-1n, -SPECIAL_EXPONENT);
}
throw new RangeError();
}
let a = create(BigInt(value), 0);

@@ -94,8 +123,20 @@ // `normalize` will change the exponent which is not good for fixed-point arithmetic (?)

//}
if (format != null) {
a = round(a, defaultRounding);
}
return a;
};
BigDecimal.toNumber = function (a) {
return Number(BigInt(BigDecimal.toBigInt(a)));
};
BigDecimal.toBigInt = function (a) {
}
function BigDecimal(significand, exponent) {
if (!(this instanceof BigDecimal)) {
return convert(significand);
}
this.significand = significand;
this.exponent = exponent;
}
const SPECIAL_EXPONENT = 1/0;
//TODO: remove
const toBigInt = function (a) {
const e = a.exponent;

@@ -115,2 +156,17 @@ const exponent = typeof e === 'number' ? e : Number(BigInt(e));

};
const abs = function (a) {
return a.significand < 0n ? BigDecimal.unaryMinus(a) : a;
};
function getCountOfDigits(a) { // floor(log(abs(a))/log(BASE)) + 1
if (a.significand === 0n) {
throw new RangeError();
}
return BigInt(digits(a.significand)) + BigInt(a.exponent);
}
function exponentiateBase(n) {
return create(BASE, n);
}
function create(significand, exponent) {

@@ -133,3 +189,3 @@ return /*Object.freeze(*/new BigDecimal(significand, exponent)/*)*/;

const s = a.toString(16);
const c = +s.charCodeAt(0) - "0".charCodeAt(0);
const c = +s.charCodeAt(0) - +"0".charCodeAt(0);
if (c <= 0) {

@@ -225,3 +281,21 @@ throw new RangeError();

function applyMaxExponent(x, rounding) {
const maximumExponent = rounding.maximumExponent;
if (maximumExponent != null) {
if (digits(x.significand) - 1 + x.exponent > maximumExponent) {
return x.significand === 0n ? create(0n, 0) : create(x.significand < 0n ? -1n : 1n, SPECIAL_EXPONENT);
}
}
return x;
}
function round(a, rounding) {
if (format === 'decimal128') {
if (rounding == null) {
return round(a, defaultRounding);
}
if (Math.abs(a.exponent) === SPECIAL_EXPONENT) {
return a;
}
}
if (rounding != null) {

@@ -239,3 +313,3 @@ let k = 0;

}
k = digits(dividend) - maximumSignificantDigits;
k = Math.max(k, digits(dividend) - maximumSignificantDigits);
}

@@ -247,3 +321,3 @@ const maximumFractionDigits = rounding.maximumFractionDigits;

}
k = 0 - sum(exponent, maximumFractionDigits);
k = Math.max(k, 0 - sum(exponent, maximumFractionDigits));
//k = Math.min(k, digits(a.significand) + 1);

@@ -336,11 +410,46 @@ //if (k < 0 && k >= -1024 && BASE === 2) {

}
return create(quotient, sum(exponent, k));
const e = sum(exponent, k);
return applyMaxExponent(create(quotient, e), rounding);
}
return applyMaxExponent(a, rounding);
}
return a;
}
BigDecimal.unaryMinus = function (a) {
if (format != null) {
if (a.exponent === -SPECIAL_EXPONENT) {
return create(0n, 0);
}
if (a.significand === 0n && a.exponent !== SPECIAL_EXPONENT) {
return create(-1n, -SPECIAL_EXPONENT);
}
}
return create(-BigInt(a.significand), a.exponent);
};
BigDecimal.add = function (a, b, rounding = null) {
function tonum(x) {
// converts +0, +-Infinity, NaN to corresponding values, and other values to sign(value)
return (x.significand < 0n ? -1 : (x.significand > 0n ? +1 : 0)) * (Math.abs(x.exponent) !== SPECIAL_EXPONENT ? 1 : Math.pow(BASE, x.exponent));
}
function fromnum(x) {
if (x !== x) {
return create(0n, SPECIAL_EXPONENT);
}
if (x === +1/0) {
return create(1n, SPECIAL_EXPONENT);
}
if (x === -1/0) {
return create(-1n, SPECIAL_EXPONENT);
}
if (x === 0 && 1/x > 0) {
return create(0n, 0);
}
if (x === 0 && 1/x < 0) {
return create(-1n, -SPECIAL_EXPONENT);
}
throw new Error(x);
}
BigDecimal.add = function (a, b, rounding = defaultRounding) {
const as = BigInt(a.significand);

@@ -350,2 +459,16 @@ const bs = BigInt(b.significand);

const be = b.exponent;
if (format != null) {
if (ae === -SPECIAL_EXPONENT || be === -SPECIAL_EXPONENT) {
if (ae === -SPECIAL_EXPONENT && be !== -SPECIAL_EXPONENT) {
return round(b, rounding);
}
if (be === -SPECIAL_EXPONENT && ae !== -SPECIAL_EXPONENT) {
return round(a, rounding);
}
return create(-1n, -SPECIAL_EXPONENT);
}
if (Math.abs(a.exponent) === SPECIAL_EXPONENT || Math.abs(b.exponent) === SPECIAL_EXPONENT) {
return fromnum(tonum(a) + tonum(b));
}
}
const bd = diff(ae, be);

@@ -376,7 +499,25 @@ const d = typeof bd === 'number' ? bd : Number(BigInt(bd));

};
BigDecimal.subtract = function (a, b, rounding = null) {
BigDecimal.subtract = function (a, b, rounding = defaultRounding) {
return BigDecimal.add(a, BigDecimal.unaryMinus(b), rounding);
};
BigDecimal.multiply = function (a, b, rounding = null) {
return normalize(round(create(BigInt(a.significand) * BigInt(b.significand), sum(a.exponent, b.exponent)), rounding), rounding);
function toSignedZero(a, b, p) {
if (format != null) {
if (p.significand === 0n || p.exponent === -SPECIAL_EXPONENT) {
if (a.significand < 0n) {
return toSignedZero(BigDecimal.unaryMinus(a), b, BigDecimal.unaryMinus(p));
}
if (b.significand < 0n) {
return toSignedZero(a, BigDecimal.unaryMinus(b), BigDecimal.unaryMinus(p));
}
}
}
return p;
}
BigDecimal.multiply = function (a, b, rounding = defaultRounding) {
if (format != null) {
if (Math.abs(a.exponent) === SPECIAL_EXPONENT || Math.abs(b.exponent) === SPECIAL_EXPONENT) {
return fromnum(tonum(a) * tonum(b));
}
}
return toSignedZero(a, b, normalize(round(create(BigInt(a.significand) * BigInt(b.significand), sum(a.exponent, b.exponent)), rounding), rounding));
};

@@ -395,5 +536,7 @@ function bigIntScale(a, scaling) {

}
BigDecimal.divide = function (a, b, rounding = null) {
if (a.significand === 0n) {
return a;
BigDecimal.divide = function (a, b, rounding = defaultRounding) {
if (format != null) {
if (Math.abs(a.exponent) === SPECIAL_EXPONENT || Math.abs(b.exponent) === SPECIAL_EXPONENT || b.significand === 0n) {
return fromnum(tonum(a) / tonum(b));
}
}

@@ -403,3 +546,3 @@ const exponent = diff(a.exponent, b.exponent);

if (rounding != null && rounding.maximumSignificantDigits != null) {
scaling = rounding.maximumSignificantDigits + (digits(b.significand) - digits(a.significand));
scaling = rounding.maximumSignificantDigits + ((b.significand === 0n ? 0 : digits(b.significand)) - (a.significand === 0n ? 0 : digits(a.significand)));
} else if (rounding != null && rounding.maximumFractionDigits != null) {

@@ -452,7 +595,21 @@ //scaling = BigInt(rounding.maximumFractionDigits) + bigIntMax(a.exponent, 0n) + bigIntMax(0n - b.exponent, 0n) - bigIntMin(a.exponent - b.exponent + BigInt(digits(a.significand) - digits(b.significand)), 0n);

}
} else {
//TODO: optimize
while (scaling >= 1 && quotient % BIGINT_BASE === 0n) {
quotient /= BIGINT_BASE;
scaling -= 1;
}
}
}
return round(create(quotient, diff(exponent, scaling)), rounding);
return toSignedZero(a, b, round(create(quotient, diff(exponent, scaling)), rounding));
};
function cmpnum(a, b) {
return a < b ? -1 : (b < a ? +1 : (a === b ? 0 : undefined));
}
function compare(a, b) {
if (format != null) {
if (Math.abs(a.exponent) === SPECIAL_EXPONENT || Math.abs(b.exponent) === SPECIAL_EXPONENT || b.significand === 0n) {
return cmpnum(tonum(a), tonum(b));
}
}
const as = BigInt(a.significand);

@@ -495,2 +652,9 @@ const bs = BigInt(b.significand);

}
BigDecimal.cmp = function (a, b) {
return compare(a, b);
};
BigDecimal.equal = function (a, b) {
return compare(a, b) === 0;
};
BigDecimal.lessThan = function (a, b) {

@@ -502,5 +666,3 @@ return compare(a, b) < 0;

};
BigDecimal.equal = function (a, b) {
return compare(a, b) === 0;
};
BigDecimal.round = function (a, rounding) {

@@ -519,4 +681,9 @@ //TODO: quick round algorithm (?)

}
const x = BigDecimal.BigDecimal(this);
const x = convert(this);
let significand = x.significand.toString();
if (format != null) {
if (Math.abs(x.exponent) === SPECIAL_EXPONENT) {
return tonum(x).toString();
}
}
//! https://tc39.es/ecma262/#sec-numeric-types-number-tostring

@@ -527,3 +694,3 @@ if (significand === "0") {

let sign = "";
if (significand.charCodeAt(0) === "-".charCodeAt(0)) {
if (+significand.charCodeAt(0) === +"-".charCodeAt(0)) {
significand = significand.slice(1);

@@ -534,6 +701,8 @@ sign = "-";

const e = typeof E === 'number' ? E : Number(BigInt(E));
const normalize = format != null ? false : true;
const minSignificant = normalize ? 0 : significand.length;
if (e > -7 && e < 21) {
return sign + bigDecimalToPlainString(significand, e + 1 - significand.length, 0, 0);
return sign + bigDecimalToPlainString(significand, e + 1 - significand.length, 0, minSignificant);
}
return sign + bigDecimalToPlainString(significand, -(significand.length - 1), 0, 0) + "e" + (e >= 0 ? "+" : "") + E.toString();
return sign + bigDecimalToPlainString(significand, -(significand.length - 1), 0, minSignificant) + "e" + (e >= 0 ? "+" : "") + E.toString();
};

@@ -551,12 +720,12 @@

while (fraction > minFraction &&
i >= minSignificant &&
i >= 1 && 0 + significand.charCodeAt(i - 1) === "0".charCodeAt(0)) {
+i >= +minSignificant &&
i >= 1 && +significand.charCodeAt(i - 1) === +"0".charCodeAt(0)) {
i -= 1;
fraction -= 1;
}
if (i < significand.length) {
if (+i < +significand.length) {
significand = significand.slice(0, i);
}
const zeros = Math.max(Math.max(0, minFraction - fraction), Math.max(0, minSignificant - significand.length));
if (zeros !== 0) {
const zeros = Math.max(minFraction - fraction, +minSignificant - +significand.length);
if (zeros > 0) {
significand += String("0".repeat(zeros));

@@ -588,7 +757,12 @@ }

}
const value = BASE === 10 ? create(this.significand, sum(this.exponent, fractionDigits)) : BigDecimal.multiply(BigDecimal.BigDecimal(10n**BigInt(fractionDigits)), this);
if (format != null) {
if (Math.abs(this.exponent) === SPECIAL_EXPONENT) {
return tonum(this).toFixed(fractionDigits);
}
}
const value = BASE === 10 ? create(this.significand, sum(this.exponent, fractionDigits)) : BigDecimal.multiply(convert(10n**BigInt(fractionDigits)), this);
const sign = value.significand < 0n ? "-" : "";
const rounded = BigDecimal.round(value, {maximumFractionDigits: 0, roundingMode: roundingMode});
const a = BigDecimal.abs(rounded);
return sign + toFixed(BigDecimal.toBigInt(a).toString(), 0 - fractionDigits, fractionDigits);
const a = abs(rounded);
return sign + toFixed(toBigInt(a).toString(), 0 - fractionDigits, fractionDigits);
};

@@ -598,3 +772,3 @@

if (BASE === 10) {
const x = BigDecimal.round(BigDecimal.abs(value), {maximumSignificantDigits: precision, roundingMode: roundingMode});
const x = BigDecimal.round(abs(value), {maximumSignificantDigits: precision, roundingMode: roundingMode});
return {significand: x.significand.toString(), exponent: BigInt(x.exponent)};

@@ -605,3 +779,3 @@ }

if (n < 0n) {
return BigDecimal.divide(BigDecimal.BigDecimal(1), exponentiate(x, -BigInt(n), rounding), rounding);
return BigDecimal.divide(convert(1), exponentiate(x, -BigInt(n), rounding), rounding);
}

@@ -618,10 +792,10 @@ let y = undefined;

}
return y == undefined ? BigDecimal.BigDecimal(1) : y;
return y == undefined ? convert(1) : y;
};
const logarithm = function (x, b, rounding) {
if (!BigDecimal.greaterThan(x, BigDecimal.BigDecimal(0))) {
if (BigDecimal.cmp(x, convert(0)) <= 0) {
throw new RangeError();
}
if (BigDecimal.lessThan(x, BigDecimal.BigDecimal(1))) {
return 0n - logarithm(BigDecimal.divide(BigDecimal.BigDecimal(1), x, rounding), b, rounding);
if (BigDecimal.cmp(x, convert(1)) < 0) {
return 0n - logarithm(BigDecimal.divide(convert(1), x, rounding), b, rounding);
}

@@ -634,3 +808,3 @@ const digits = getCountOfDigits(x);

}
return log + logarithm(BigDecimal.divide(x, exponentiate(BigDecimal.BigDecimal(b), log, rounding), rounding), b, rounding);
return log + logarithm(BigDecimal.divide(x, exponentiate(convert(b), log, rounding), rounding), b, rounding);
};

@@ -641,6 +815,6 @@ const sign = value.significand < 0n ? -1 : +1;

};
if (BigDecimal.equal(value, BigDecimal.BigDecimal(0))) {
if (BigDecimal.cmp(value, convert(0)) === 0) {
return {significand: "0", exponent: 0n};
}
const ten = BigDecimal.BigDecimal(10);
const ten = convert(10);
const minimumSignificantDigits = Math.pow(2, Math.ceil(Math.log2(bitLength(bigIntAbs(BigInt(value.exponent)) + 1n) * BASE_LOG2_INV)));

@@ -651,26 +825,26 @@ let rounding = {maximumSignificantDigits: Math.max(minimumSignificantDigits, BASE === 2 ? 32 : 8), roundingMode: "half-even"};

do {
let x = BigDecimal.abs(value);
let x = abs(value);
fd = 0n - logarithm(x, 10, rounding);
x = BigDecimal.multiply(exponentiate(ten, fd, rounding), x, rounding);
if (!BigDecimal.lessThan(x, ten)) {
if (BigDecimal.cmp(x, ten) >= 0) {
fd -= 1n;
x = BigDecimal.divide(x, ten, rounding);
}
if (BigDecimal.lessThan(x, BigDecimal.BigDecimal(1))) {
if (BigDecimal.cmp(x, convert(1)) < 0) {
fd += 1n;
x = BigDecimal.multiply(x, ten, rounding);
}
if (!BigDecimal.lessThan(x, BigDecimal.BigDecimal(1)) && BigDecimal.lessThan(x, ten)) {
if (BigDecimal.cmp(x, convert(1)) >= 0 && BigDecimal.cmp(x, ten) < 0) {
fd += BigInt(precision - 1);
//TODO: ?
if (rounding.maximumSignificantDigits > (Math.abs(Number(fd)) + precision) * Math.log2(10) + digits(value.significand)) {
x = BigDecimal.abs(value);
x = abs(value);
x = BigDecimal.multiply(x, exponentiate(ten, fd, rounding), rounding)
result = BigDecimal.toBigInt(BigDecimal.abs(roundToInteger(x))).toString();
result = toBigInt(abs(roundToInteger(x))).toString();
} else {
x = BigDecimal.multiply(x, exponentiate(ten, BigInt(precision - 1), rounding), rounding);
x = BigDecimal.abs(x);
const error = BigDecimal.multiply(BigDecimal.multiply(BigDecimal.BigDecimal(bigIntAbs(fd) + BigInt(precision)), exponentiateBase(BASE, 0 - rounding.maximumSignificantDigits)), x);
if (BigDecimal.equal(roundToInteger(BigDecimal.add(x, error)), roundToInteger(BigDecimal.subtract(x, error)))) {
result = BigDecimal.toBigInt(BigDecimal.abs(roundToInteger(x))).toString();
x = abs(x);
const error = BigDecimal.multiply(BigDecimal.multiply(convert(bigIntAbs(fd) + BigInt(precision)), exponentiateBase(0 - rounding.maximumSignificantDigits)), x);
if (BigDecimal.cmp(roundToInteger(BigDecimal.add(x, error)), roundToInteger(BigDecimal.subtract(x, error))) === 0) {
result = toBigInt(abs(roundToInteger(x))).toString();
}

@@ -685,341 +859,19 @@ }

BigDecimal.prototype.toPrecision = function (precision, roundingMode = "half-up") {
if (format != null) {
if (Math.abs(this.exponent) === SPECIAL_EXPONENT) {
return tonum(this).toPrecision(precision);
}
}
const tmp = getDecimalSignificantAndExponent(this, precision, roundingMode);
return (BigDecimal.lessThan(this, BigDecimal.BigDecimal(0)) ? "-" : "") + toPrecision(tmp.significand, tmp.exponent, precision);
return (BigDecimal.cmp(this, convert(0)) < 0 ? "-" : "") + toPrecision(tmp.significand, tmp.exponent, precision);
};
BigDecimal.prototype.toExponential = function (fractionDigits, roundingMode = "half-up") {
const tmp = getDecimalSignificantAndExponent(this, fractionDigits + 1, roundingMode);
return (BigDecimal.lessThan(this, BigDecimal.BigDecimal(0)) ? "-" : "") + toExponential(tmp.significand, tmp.exponent, fractionDigits);
};
function exponentiate(a, n) {
if (+a !== BASE) {
throw new RangeError("a should be BASE");//?
}
return create(1n, n);
}
function exponentiateBase(a, n) {
return exponentiate(a, n);
}
function getCountOfDigits(a) { // floor(log(abs(a))/log(BASE)) + 1
if (a.significand === 0n) {
throw new RangeError();
}
return BigInt(digits(a.significand)) + BigInt(a.exponent);
}
BigDecimal.abs = function (a) {
return a.significand < 0n ? BigDecimal.unaryMinus(a) : a;
};
BigDecimal.sign = function (a) {
return a.significand < 0n ? -1 : (a.significand > 0n ? +1 : 0);
};
BigDecimal.max = function (a, b) {
if (arguments.length > 2) {
throw new RangeError("not implemented");
}
return BigDecimal.lessThan(a, b) ? b : a;
};
BigDecimal.min = function (a, b) {
if (arguments.length > 2) {
throw new RangeError("not implemented");
}
return BigDecimal.greaterThan(a, b) ? b : a;
};
function significandDigits(a) {
let maximumSignificantDigits = 1;
while (!BigDecimal.equal(BigDecimal.round(a, {maximumSignificantDigits: maximumSignificantDigits, roundingMode: "half-even"}), a)) {
maximumSignificantDigits *= 2;
}
let from = maximumSignificantDigits / 2;
let to = maximumSignificantDigits;
while (to - 1 > from) {
const middle = from + Math.floor((to - from) / 2);
if (!BigDecimal.equal(BigDecimal.round(a, {maximumSignificantDigits: middle, roundingMode: "half-even"}), a)) {
from = middle;
} else {
to = middle;
if (format != null) {
if (Math.abs(this.exponent) === SPECIAL_EXPONENT) {
return tonum(this).toExponential(fractionDigits);
}
}
return to;
}
function getExponent(number) {
const e = Math.floor(Math.log(Math.abs(number)) / Math.log(2)) - 1;
return Math.abs(number) / 2**e >= 2 ? e + 1 : e;
}
function tryToMakeCorrectlyRounded(specialValue, f, name) {
function getExpectedResultIntegerDigits(x) {
if (name === "exp") {
// e**x <= BASE**k
// k >= x / log(BASE)
return Math.ceil(Number(BigInt(BigDecimal.toBigInt(BigDecimal.round(x, {maximumFractionDigits: 0, roundingMode: "half-even"})))) / Math.log(BASE));
}
if (name === "log") {
// log(x) <= BASE**k
// log(log(x))/log(BASE) <= k
return Math.ceil(Math.log2(Math.ceil(Math.max(Number(getCountOfDigits(x)), 1) * Math.log(BASE))) * BASE_LOG2_INV);
}
return 1;
}
// (?) https://en.wikipedia.org/wiki/Rounding#Table-maker's_dilemma
return function (x, rounding) {
if (BigDecimal.equal(x, BigDecimal.BigDecimal(specialValue))) {
return f(x, {maximumSignificantDigits: 1, roundingMode: "half-even"});
}
let result = BigDecimal.BigDecimal(0);
let i = 0;
let error = BigDecimal.BigDecimal(0);
do {
if (i > 4 * ((9 + 1) / BASE) && rounding.maximumSignificantDigits != null && rounding.roundingMode === "half-even" && name !== "sin" && name !== "cos") {
console.error(x, rounding);
throw new Error();
}
i += 1;
const internalRounding = {
maximumSignificantDigits: Math.ceil(Math.max(rounding.maximumSignificantDigits || (rounding.maximumFractionDigits + 1 + getExpectedResultIntegerDigits(x) - 1), significandDigits(x)) * Math.pow(2, Math.ceil((i - 1) / 3))) + 2 + (BASE === 2 ? 1 : 0),
roundingMode: "half-even"
};
result = undefined;
if (BASE === 2 && Math.max(internalRounding.maximumSignificantDigits + 2, significandDigits(x) + 1) <= Math.log2(Number.MAX_SAFE_INTEGER + 1)) {
// Hm... https://www.gnu.org/software/libc/manual/html_node/Errors-in-Math-Functions.html
const e = x.exponent;
const exponent = typeof e === 'number' ? e : Number(BigInt(e));
const v = Number(BigInt(x.significand)) * BASE**exponent;
// some browsers have inaccurate results for Math.sin, Math.cos, Math.tan outside of [-pi/4;pi/4] range
if ((name !== "sin" && name !== "cos" && name !== "tan") || Math.abs(v) <= Math.PI / 4) {
const numberValue = Math[name](v);
const MIN_NORMALIZED_VALUE = (Number.MIN_VALUE * 1.25 > Number.MIN_VALUE ? Number.MIN_VALUE : Number.MIN_VALUE * (Number.MAX_SAFE_INTEGER + 1) / 2) || 2**-1022;
const a = Math.abs(numberValue);
if (a < 1/0 && a > MIN_NORMALIZED_VALUE) {
result = BigDecimal.BigDecimal(numberValue);
}
}
}
if (result == undefined) {
result = f(x, internalRounding);
}
// round(result - error) === round(result + error)
error = BigDecimal.multiply(exponentiate(BASE, -BigInt(internalRounding.maximumSignificantDigits)), BigDecimal.abs(result));
//if (i > 0) {
//console.log(i, f.name, x + "", result + "", error + "", BigDecimal.round(BigDecimal.subtract(result, error), rounding) + "", BigDecimal.round(BigDecimal.add(result, error), rounding) + "");
//}
} while (!BigDecimal.equal(BigDecimal.round(BigDecimal.subtract(result, error), rounding), BigDecimal.round(BigDecimal.add(result, error), rounding)));
if (i > 1) {
//console.debug(i, name);
}
return BigDecimal.round(result, rounding);
};
}
function sqrt(x, rounding) {
// from https://en.wikipedia.org/wiki/Square_root#Computation
let lastResult = BigDecimal.add(x, BigDecimal.BigDecimal(1));
let result = x;
while (BigDecimal.lessThan(result, lastResult)) {
lastResult = result;
result = BigDecimal.divide(BigDecimal.add(BigDecimal.divide(x, result, rounding), result), BigDecimal.BigDecimal(2), rounding);
}
return result;
}
BigDecimal.log = tryToMakeCorrectlyRounded(1, function log(x, rounding) {
if (!BigDecimal.greaterThan(x, BigDecimal.BigDecimal(0))) {
throw new RangeError();
}
// https://ru.wikipedia.org/wiki/Логарифм#Разложение_в_ряд_и_вычисление_натурального_логарифма
const internalRounding = {
maximumSignificantDigits: rounding.maximumSignificantDigits + Math.ceil(Math.log2(rounding.maximumSignificantDigits + 0.5) * BASE_LOG2_INV),
roundingMode: "half-even"
};
if (true) {
//! ln(f * BASE**k) = ln(f) + k * ln(BASE), where (1/BASE) <= f <= BASE
let k = getCountOfDigits(x) - 1n;
let f = BigDecimal.multiply(exponentiate(BASE, -k), x);
let ff = BigDecimal.round(BigDecimal.multiply(f, f), {maximumSignificantDigits: 3, roundingMode: "half-even"});
if (BigDecimal.greaterThan(ff, exponentiate(BASE, 1n))) {
k += 1n;
f = BigDecimal.multiply(exponentiate(BASE, -1n), f);
}
if (BigDecimal.lessThan(ff, exponentiate(BASE, -1n))) {
k -= 1n;
f = BigDecimal.multiply(exponentiate(BASE, 1n), f);
}
if (k !== 0n) {
return BigDecimal.add(BigDecimal.log(f, internalRounding), BigDecimal.multiply(BigDecimal.BigDecimal(2n * k), BigDecimal.log(sqrt(BigDecimal.BigDecimal(BASE), internalRounding), internalRounding)));
}
}
//! log(x) = log((1 + g) / (1 - g)) = 2*(g + g**3/3 + g**5/5 + ...)
const g = BigDecimal.divide(BigDecimal.subtract(x, BigDecimal.BigDecimal(1)), BigDecimal.add(x, BigDecimal.BigDecimal(1)), internalRounding);
let n = 1;
let term = BigDecimal.BigDecimal(1);
let sum = term;
let lastSum = BigDecimal.BigDecimal(0);
const gg = BigDecimal.multiply(g, g, internalRounding);
while (!BigDecimal.equal(lastSum, sum)) {
n += 2;
term = BigDecimal.multiply(term, BigDecimal.BigDecimal(n - 2));
term = BigDecimal.multiply(term, gg);
term = BigDecimal.divide(term, BigDecimal.BigDecimal(n), internalRounding);
lastSum = sum;
sum = BigDecimal.add(sum, term, internalRounding);
}
return BigDecimal.multiply(BigDecimal.multiply(BigDecimal.BigDecimal(2), g), sum);
}, "log");
function fromNumberApproximate(number) {
return BigDecimal.divide(BigDecimal.BigDecimal(Math.floor(number * (Number.MAX_SAFE_INTEGER + 1))),
BigDecimal.add(BigDecimal.BigDecimal(Number.MAX_SAFE_INTEGER), BigDecimal.BigDecimal(1)),
{maximumSignificantDigits: Math.floor(Math.log2(Number.MAX_SAFE_INTEGER + 1) * BASE_LOG2_INV + 0.5), roundingMode: "half-even"});
}
BigDecimal.exp = tryToMakeCorrectlyRounded(0, function exp(x, rounding) {
//! k = round(x / ln(BASE));
//! exp(x) = exp(x - k * ln(BASE) + k * ln(BASE)) = exp(x - k * ln(BASE)) * BASE**k
const internalRounding = {
maximumSignificantDigits: rounding.maximumSignificantDigits + Math.ceil(Math.log2(rounding.maximumSignificantDigits + 0.5) * BASE_LOG2_INV),
roundingMode: "half-even"
};
if (!BigDecimal.equal(x, BigDecimal.BigDecimal(0))) {
const logBASEApproximate = fromNumberApproximate(Math.log(BASE));
const kApproximate = BigDecimal.round(BigDecimal.divide(x, logBASEApproximate, {maximumSignificantDigits: Math.max(Number(getCountOfDigits(x)), 1), roundingMode: "half-even"}), {maximumFractionDigits: 0, roundingMode: "half-even"});
if (!BigDecimal.equal(kApproximate, BigDecimal.BigDecimal(0))) {
const logBASE = BigDecimal.log(BigDecimal.BigDecimal(BASE), {maximumSignificantDigits: internalRounding.maximumSignificantDigits + Number(getCountOfDigits(kApproximate)), roundingMode: "half-even"});
const k = BigDecimal.round(BigDecimal.divide(x, logBASE, {maximumSignificantDigits: Math.max(Number(getCountOfDigits(x)), 1), roundingMode: "half-even"}), {maximumFractionDigits: 0, roundingMode: "half-even"});
if (!BigDecimal.equal(k, BigDecimal.BigDecimal(0))) {
const r = BigDecimal.subtract(x, BigDecimal.multiply(k, logBASE));
return BigDecimal.multiply(exponentiate(BASE, BigDecimal.toBigInt(k)), BigDecimal.exp(r, internalRounding));
}
}
}
// https://en.wikipedia.org/wiki/Exponential_function#Computation
let n = 0;
let term = BigDecimal.BigDecimal(1);
let sum = term;
let lastSum = BigDecimal.BigDecimal(0);
while (!BigDecimal.equal(lastSum, sum)) {
n += 1;
term = BigDecimal.multiply(term, x);
term = BigDecimal.divide(term, BigDecimal.BigDecimal(n), internalRounding);
lastSum = sum;
sum = BigDecimal.add(sum, term, internalRounding);
}
return sum;
}, "exp");
function divideByHalfOfPI(x, rounding) { // x = k*pi/2 + r + 2*pi*n, where |r| < pi/4
const quarterOfPiApproximated = fromNumberApproximate(Math.PI / 4);
if (BigDecimal.greaterThan(BigDecimal.abs(x), quarterOfPiApproximated)) {
//TODO: FIX
const internalRounding = {
maximumSignificantDigits: rounding.maximumSignificantDigits + significandDigits(x) + Number(getCountOfDigits(x)) + 1 + Math.ceil(42 * BASE_LOG2_INV),
roundingMode: "half-even"
};
const halfOfPi = BigDecimal.multiply(BigDecimal.BigDecimal(2), BigDecimal.atan(BigDecimal.BigDecimal(1), internalRounding));
const i = BigDecimal.round(BigDecimal.divide(x, halfOfPi, {maximumSignificantDigits: Math.max(Number(getCountOfDigits(x)), 1), roundingMode: "half-even"}), {maximumFractionDigits: 0, roundingMode: "half-even"});
const remainder = BigDecimal.subtract(x, BigDecimal.multiply(i, halfOfPi));
return {remainder: remainder, k: (Number(BigDecimal.toBigInt(i) % 4n) + 4) % 4};
}
return {remainder: x, k: 0};
}
function _cos(x, rounding, subtractHalfOfPi) {
const tmp = divideByHalfOfPI(x, rounding);
const a = tmp.remainder;
const k = (tmp.k + (subtractHalfOfPi ? -1 + 4 : 0)) % 4;
// https://en.wikipedia.org/wiki/Lookup_table#Computing_sines
// https://en.wikipedia.org/wiki/Trigonometric_functions#Power_series_expansion
const internalRounding = {
maximumSignificantDigits: rounding.maximumSignificantDigits + Math.ceil(Math.log2(rounding.maximumSignificantDigits + 0.5) * BASE_LOG2_INV),
roundingMode: "half-even"
};
let n = k === 1 || k === 3 ? 1 : 0;
let term = BigDecimal.BigDecimal(1);
let sum = term;
let lastSum = BigDecimal.BigDecimal(0);
const aa = BigDecimal.multiply(a, a);
while (!BigDecimal.equal(lastSum, sum)) {
n += 2;
term = BigDecimal.multiply(term, aa);
term = BigDecimal.divide(term, BigDecimal.BigDecimal(-n * (n - 1)), internalRounding);
lastSum = sum;
sum = BigDecimal.add(sum, term, internalRounding);
}
if (k === 1 || k === 2) {
sum = BigDecimal.unaryMinus(sum);
}
return k === 1 || k === 3 ? BigDecimal.multiply(a, sum) : sum;
}
BigDecimal.sin = tryToMakeCorrectlyRounded(0, function (x, rounding) {
return _cos(x, rounding, true);
}, "sin");
BigDecimal.cos = tryToMakeCorrectlyRounded(0, function (x, rounding) {
return _cos(x, rounding, false);
}, "cos");
BigDecimal.atan = tryToMakeCorrectlyRounded(0, function (x, rounding) {
if (BigDecimal.greaterThan(BigDecimal.abs(x), BigDecimal.BigDecimal(1))) {
//Note: rounding to maximumFractionDigits
const internalRounding = {
maximumFractionDigits: rounding.maximumSignificantDigits + 1,
roundingMode: "half-even"
};
const halfOfPi = BigDecimal.multiply(BigDecimal.atan(BigDecimal.BigDecimal(1), internalRounding), BigDecimal.BigDecimal(2));
return BigDecimal.multiply(BigDecimal.BigDecimal(BigDecimal.lessThan(x, BigDecimal.BigDecimal(0)) ? -1 : +1), BigDecimal.subtract(halfOfPi, BigDecimal.atan(BigDecimal.divide(BigDecimal.BigDecimal(1), BigDecimal.abs(x), internalRounding), internalRounding)));
}
// https://en.wikipedia.org/wiki/Inverse_trigonometric_functions#:~:text=Alternatively,%20this%20can%20be%20expressed%20as
const internalRounding = {
maximumSignificantDigits: rounding.maximumSignificantDigits + Math.ceil(Math.log2(rounding.maximumSignificantDigits + 0.5) * BASE_LOG2_INV),
roundingMode: "half-even"
};
let n = 0;
const xx = BigDecimal.multiply(x, x);
const xxplus1 = BigDecimal.add(BigDecimal.BigDecimal(1), xx);
let term = BigDecimal.divide(BigDecimal.BigDecimal(1), xxplus1, internalRounding);
let sum = term;
let lastSum = BigDecimal.BigDecimal(0);
while (!BigDecimal.equal(lastSum, sum)) {
n += 1;
term = BigDecimal.multiply(term, BigDecimal.multiply(BigDecimal.BigDecimal(2 * n), xx));
term = BigDecimal.divide(term, BigDecimal.multiply(BigDecimal.BigDecimal(2 * n + 1), xxplus1), internalRounding);
lastSum = sum;
sum = BigDecimal.add(sum, term, internalRounding);
}
return BigDecimal.multiply(x, sum);
}, "atan");
BigDecimal.sqrt = function (x, rounding) {
if (BigDecimal.lessThan(x, BigDecimal.BigDecimal(0))) {
throw new RangeError();
}
if (BigDecimal.equal(x, BigDecimal.BigDecimal(0))) {
return x;
}
// https://en.wikipedia.org/wiki/Nth_root#Using_Newton's_method
const e = getCountOfDigits(x) / 2n;
const t = exponentiate(BASE, e);
const y = BigDecimal.multiply(x, exponentiate(BASE, -(2n * e)));
const k = Math.floor(Math.log2(Number.MAX_SAFE_INTEGER + 1) * BASE_LOG2_INV) - 1;
const xn = BigDecimal.toNumber(BigDecimal.round(BigDecimal.multiply(y, exponentiate(BASE, k)), {maximumFractionDigits: 0, roundingMode: "half-even"})) / BASE**k;
const r = Math.sqrt(xn);
//TODO: fix
const resultSignificantDigits = 2 * (rounding.maximumSignificantDigits || (rounding.maximumFractionDigits + Math.ceil(significandDigits(x) / 2)) || 1);
let result = BigDecimal.multiply(BigDecimal.BigDecimal(Math.sign(r) * Math.floor(Math.abs(r) * BASE**k + 0.5)), exponentiate(BASE, -k));
const iteration = function (result, internalRounding) {
return BigDecimal.divide(BigDecimal.add(y, BigDecimal.multiply(result, result)), BigDecimal.multiply(BigDecimal.BigDecimal(2), result), internalRounding);
};
for (let i = Math.max(k - 1, 1); i <= resultSignificantDigits; i *= 2) {
const internalRounding = {maximumSignificantDigits: i, roundingMode: "half-even"};
result = iteration(result, internalRounding);
}
result = iteration(result, rounding);
return BigDecimal.multiply(result, t);
const tmp = getDecimalSignificantAndExponent(this, fractionDigits + 1, roundingMode);
return (BigDecimal.cmp(this, convert(0)) < 0 ? "-" : "") + toExponential(tmp.significand, tmp.exponent, fractionDigits);
};

@@ -1032,3 +884,4 @@

const BigFloat = factory(2);
const Decimal128 = factory(10, 'decimal128');
export {BigDecimal, BigFloat};
export {BigDecimal, BigFloat, Decimal128};
{
"name": "@yaffle/bigdecimal",
"version": "1.0.36",
"version": "2.0.0",
"description": "Arbitrary precision decimal arithmetic library. Polyfill for decimal proposal. Implemented on the top of BigInt.",

@@ -23,6 +23,7 @@ "main": "BigDecimal.js",

"BigDecimal.js",
"BigDecimalMath.js",
"BigDecimal.d.ts"
],
"author": "Viktor Mukhachev",
"license": "ISC",
"license": "$$$",
"bugs": {

@@ -34,4 +35,5 @@ "url": "https://github.com/Yaffle/BigDecimal/issues"

"devDependencies": {
"decimal.js": "^10.2.0"
"decimal.js": "^10.2.0",
"http-server": "^14.1.1"
}
}

@@ -20,4 +20,2 @@ # BigDecimal

a.toExponential(fractionDigits[, roundingMode = "half-up"])
BigDecimal.toBigInt(a) // (not in the spec)
BigDecimal.toNumber(a) // (not in the spec, only integers)

@@ -36,7 +34,8 @@

BigDecimal.equal(a, b)
BigDecimal.lessThan(a, b)
BigDecimal.greaterThan(a, b)
BigDecimal.cmp(a, b)
## Math: (not in the spec)
To use:
import addMath from './BigDecimalMath.js';
addMath(BigDecimal, 10);

@@ -43,0 +42,0 @@ BigDecimal.abs(a)

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc