From ef184fe2582b09f89b0efa5f011bd92832bccc69 Mon Sep 17 00:00:00 2001 From: liujia178 Date: Fri, 18 Apr 2025 21:31:31 +0800 Subject: [PATCH] Add ArkTS SDK: Decimal Issue:https://gitee.com/openharmony/commonlibrary_ets_utils/issues/IC2BY6 Signed-off-by: liujia178 --- sdk/arkts/@arkts.math.Decimal.ets | 1514 +++++++++++++++++++++++++++++ 1 file changed, 1514 insertions(+) diff --git a/sdk/arkts/@arkts.math.Decimal.ets b/sdk/arkts/@arkts.math.Decimal.ets index 57f62fbe..1106c1c6 100644 --- a/sdk/arkts/@arkts.math.Decimal.ets +++ b/sdk/arkts/@arkts.math.Decimal.ets @@ -13,5 +13,1519 @@ * limitations under the License. */ +import { BusinessError } from "@ohos.base"; + export type Rounding = number export type Modulo = number +type DecimalValue = Decimal | number | string; + +const RANGE_ERROR_CODE = 10200001; +const TYPE_ERROR_CODE = 401; +const PRECISION_LIMIT_EXCEEDED_ERROR_CODE = 10200060; + +const EXP_LIMIT: number = 9e15; +const MAX_DIGITS: number = 1e9; +const NUMERALS: string = '0123456789abcdef'; + +const LN10 = ('2.302585092994045684017991454684364207601101488628772976033327900967572609677352480235997205089598298341' + + '9677840422862486334095254650828067566662873690987816894829072083255546808437998948262331985283935053089653777326' + + '2884616336622228769821988674654366747440424327436515504893431493939147961940440022210510171417480036880840126470' + + '8068556774321622835522011480466371565912137345074785694768346361679210180644507064800027750268491674655058685693' + + '5673420670581136429224554405758925724208241314695689016758940256776311356919292033376587141660230105703089634572' + + '0754403708474699401682692828084811842893148485249486448719278096762712757753970276686059524967166741834857044225' + + '0719796500471495105049221477656763693866297697952211071826454973477266242570942932258279850258550978526538320760' + + '6726317164309505995087807523710333101197857547331541421808427543863591778117054309827482385045648019095610299291' + + '8243182375253577097505395651876975103749708886921802051893395072385392051446341972652872869651108625714921988499' + + '78748873771345686209167058'); + +let external: boolean = true; +const tag: string = '[object Decimal]'; + +const isBinary: RegExp = new RegExp("^0b([01]+(\\.[01]*)?|\\.[01]+)(p[+-]?\\d+)?$", "i"); +const isHex: RegExp = new RegExp("^0x([0-9a-f]+(\\.[0-9a-f]*)?|\\.[0-9a-f]+)(p[+-]?\\d+)?$", "i"); +const isOctal: RegExp = new RegExp("^0o([0-7]+(\\.[0-7]*)?|\\.[0-7]+)(p[+-]?\\d+)?$", "i"); +const isDecimal: RegExp = new RegExp("^(\\d+(\\.\\d*)?|\\.\\d+)(e[+-]?\\d+)?$", "i"); + +const BASE: number = 1e7; +const LOG_BASE: number = 7; +const MAX_SAFE_INTEGER: number = 9007199254740991; + +const LN10_PRECISION = LN10.length - 1; + +export interface DecimalConfig { + precision?: number; + rounding?: number; + toExpNeg?: number; + toExpPos?: number; + minE?: number; + maxE?: number; + crypto?: boolean; + modulo?: number; + defaults?: boolean; +} + +export class Decimal { + internal digits: Array | null = new Array(); + internal exponent: number; + internal sign: number; + internal toStringTag: string = '[object Decimal]'; + + public static maxE: number = EXP_LIMIT; + public static minE: number = -EXP_LIMIT; + public static precision: number = 20; + public static rounding: number = 4; + public static toExpNeg: number = -7; + public static toExpPos: number = 21; + + get d(): Array | null { + return this.digits; + } + + get e(): number { + return this.exponent; + } + + get s(): number { + return this.sign; + } + + constructor(v: DecimalValue) { + if (Utils.isDecimalInstance(v)) { + this.initializeByDecimal(v as Decimal); + } else if (typeof v === 'number') { + this.initializeByNumber(v as number); + } else if (typeof v === 'string') { + this.initializeByString(v as string); + } else { + Utils.throwBusinessError(`The type of "index" must be String. Received value is: ${v}`, TYPE_ERROR_CODE); + } + } + + public isFinite(): boolean { + return !!this.digits; + } + + public isZero (): boolean { + return !!this.digits && this.digits![0] === 0; + } + + public toDecimalPlaces(): Decimal { + return new Decimal(this); + } + + public toDecimalPlaces(decimalPlaces: number): Decimal { + return this.toDecimalPlaces(decimalPlaces, Decimal.rounding); + } + + public toDecimalPlaces(decimalPlaces: number, rounding: Rounding): Decimal { + let decimal = this.toDecimalPlaces(); + Utils.checkInt32(decimalPlaces, 0, MAX_DIGITS); + Utils.checkInt32(rounding, 0, 8); + return this.finalise(decimal, decimalPlaces + decimal.e + 1, rounding); + } + + public toNumber(): number { + return Utils.toNumber(this.valueOf()); + } + + public toString(): string { + let str = this.finiteToString(this.exponent <= Decimal.toExpNeg || this.exponent >= Decimal.toExpPos); + return this.isNeg() && !this.isZero() ? '-' + str : str; + } + + public valueOf(): string { + let str = this.finiteToString(this.exponent <= Decimal.toExpNeg || this.exponent >= Decimal.toExpPos); + return this.isNeg() ? '-' + str : str; + } + + static hypot(...n: DecimalValue[]): Decimal { + let t = new Decimal(0); + external = false; + for (let i = 0; i < n.length;) { + let v = new Decimal(n[i++]); + if (!v.d) { + if (v.s) { + external = true; + return new Decimal(Infinity); + } + t = v; + } else if (t.d) { + t = t.add(v.mul(v)); + } + } + external = true; + return t.sqrt(); + } + + public sqrt(): Decimal { + let x = this; + let d = x.d; + let s: number = x.s; + + if (s !== 1 || !d || !d![0]) { + return new Decimal(!s || s < 0 && (!d || d![0]) ? NaN : d ? x : Infinity); + } + + external = false; + s = Math.sqrt(+(x.toNumber())); + let n: string; + let e: number = x.e; + let r: Decimal; + if (s == 0 || s == Infinity) { + n = Utils.digitsToString(d); + if ((n.length + e) % 2 == 0) { + n += '0'; + } + s = Math.sqrt(Utils.toNumber(n)); + e = Math.floor((e + 1) / 2) - ((e < 0 || e % 2) as number); + if (s == Infinity) { + n = '5e' + e; + } else { + n = Utils.toExponential(s); + n = n.slice(0, n.indexOf('e') + 1) + e; + } + r = new Decimal(n); + } else { + r = new Decimal(Utils.toString(s)); + } + + let sd = (e = Decimal.precision) + 3; + let rep: number = 0; + let m: boolean = false; + for (;;) { + let t = r; + r = t.add(this.divide(x, t, sd + 2, 1)).mul(0.5); + if (Utils.digitsToString(t.d!).slice(0, sd) === (n = Utils.digitsToString(r.d!)).slice(0, sd)) { + n = n.slice(sd - 3, sd + 1); + if (n == '9999' || !rep && n == '4999') { + if (!rep) { + this.finalise(t, e + 1, 0); + if (t.mul(t).equals(x.toNumber())) { + r = t; + break; + } + } + sd += 4; + rep = 1; + } else { + if (Utils.toNumber(n) > 0 || Utils.toNumber(n.slice(1)) > 0 + && (n.charAt(0) as String).equals('5')) { + this.finalise(r, e + 1, 1); + m = !r.mul(r).equals(x.toNumber()); + } + break; + } + } + } + external = true; + return this.finalise(r, e, Decimal.rounding, m); + } + + public equals(n: number): boolean { + return this.comparedTo(n) === 0; + } + + public comparedTo(n: DecimalValue): number { + let y = new Decimal(n); + let xd = this.d; + let yd = y.d; + let xs = this.s; + let ys = y.s; + + if (!xd || !yd) { + return (!xs || !ys) ? NaN : (xs !== ys) ? xs : (xd === yd) ? 0 : (!xd ^ xs < 0) ? 1 : -1; + } + + if (!xd![0] || !yd![0]) { + return xd![0] ? xs : yd![0] ? -ys : 0; + } + + if (xs !== ys) { + return xs; + } + + if (this.e !== y.e) { + return this.e > y.e ^ xs < 0 ? 1 : -1; + } + + let xdL = xd.length; + let ydL = yd.length; + + for (let i = 0, j = xdL < ydL ? xdL : ydL; i < j; ++i) { + if (xd![i] !== yd![i]) { + return xd![i] > yd![i] ^ xs < 0 ? 1 : -1; + } + } + return xdL === ydL ? 0 : xdL > ydL ^ xs < 0 ? 1 : -1; + } + + public div(n: DecimalValue): Decimal { + return this.divide(this, new Decimal(n)); + } + + public pow(n: DecimalValue): Decimal { + let y = new Decimal(n); + let yn = +y.toNumber(); + if (!this.d || !y.d || !this.d![0] || !y.d![0]) { + return new Decimal(Math.pow(+this.toNumber(), yn)); + } + + let x = new Decimal(this); + if (x.equals(1)) { + return x; + } + + let pr = Decimal.precision; + let rm = Decimal.rounding; + if (y.equals(1)) { + return this.finalise(x, pr, rm); + } + let e = Math.floor(y.e / LOG_BASE); + let k: number; + if (e >= y.d!.length - 1 && (k = yn < 0 ? -yn : yn) <= MAX_SAFE_INTEGER) { + let r = this.intPow(x, k, pr); + return y.s < 0 ? new Decimal(1).div(r) : this.finalise(r, pr, rm); + } + + let s: number = x.s; + if (s < 0) { + if (e < y.d!.length - 1) { + return new Decimal(NaN); + } + if ((y.d![e] & 1) == 0) { + s = 1; + } + if (x.e == 0 && x.d![0] == 1 && x.d!.length == 1) { + x.sign = s; + return x; + } + } + + k = Math.pow(+x.toNumber(), yn); + e = k == 0 || !isFinite(k) + ? Math.floor(yn * (Math.log(Utils.toNumber('0.' + Utils.digitsToString(x.d!))) / Math.LN10 + x.e + 1)) + : new Decimal(k + '').e; + + if (e > Decimal.maxE + 1 || e < Decimal.minE - 1) { + return new Decimal(e > 0 ? s / 0 : 0); + } + + external = false; + Decimal.rounding = x.sign = 1; + k = Math.min(12, (e + '').length); + let r = this.naturalExponential(y.mul(this.naturalLogarithm(x, pr + k)), pr); + if (r.d) { + r = this.finalise(r, pr + 5, 1); + if (Utils.checkRoundingDigits(r.d!, pr, rm)) { + e = pr + 10; + r = this.finalise(this.naturalExponential(y.mul(this.naturalLogarithm(x, e + k)), e), e + 5, 1); + if (Utils.toNumber(Utils.digitsToString(r.d!).slice(pr + 1, pr + 15)) + 1 == 1e14) { + r = this.finalise(r, pr + 1, 0); + } + } + } + r.sign = s; + external = true; + Decimal.rounding = rm; + return this.finalise(r, pr, rm); + } + + public exp(): Decimal { + return this.naturalExponential(this); + } + + public add(n: DecimalValue): Decimal { + let y = new Decimal(n); + if (!this.d || !this.d) { + if (!this.s || !y.s) { + y = new Decimal(NaN); + } else if (!this.d) { + y = new Decimal(y.d || this.s === y.s ? this : NaN); + } + return y; + } + + if (this.s != y.s) { + y.sign = -y.s; + return this.sub(y); + } + let xd = this.d; + let yd = y.d; + let pr = Decimal.precision; + let rm = Decimal.rounding; + + if (!xd![0] || !yd![0]) { + if (!yd![0]) { + y = new Decimal(this); + } + return external ? this.finalise(y, pr, rm) : y; + } + let k = Math.floor(this.e / LOG_BASE); + let e = Math.floor(y.e / LOG_BASE); + xd = xd!.slice(); + let i = k - e; + let len: number; + let d: Array; + if (i) { + if (i < 0) { + d = xd; + i = -i; + len = yd!.length; + } else { + d = yd!; + e = k; + len = xd.length; + } + k = Math.ceil(pr / LOG_BASE); + len = k > len ? k + 1 : len + 1; + if (i > len) { + i = len; + d.length = 1; + } + d.reverse(); + for (; i--;) { + d.push(0); + } + d.reverse(); + } + len = xd.length; + i = yd!.length; + if (len - i < 0) { + i = len; + d = yd!; + yd = xd; + xd = d; + } + let carry: number = 0; + for (carry = 0; i;) { + --i; + xd[i] = xd[i] + yd![i] + carry; + carry = (xd[i] / BASE) | 0; + xd[i] = xd[i] % BASE; + } + + if (carry) { + xd.unshift(carry); + ++e; + } + + for (len = xd.length; xd[--len] == 0;) { + xd.pop(); + } + y.digits = xd; + y.exponent = this.getBase10Exponent(xd, e); + return external ? this.finalise(y, pr, rm) : y; + } + + public sub(n: DecimalValue): Decimal { + let y: Decimal = new Decimal(n); + let x = this; + if (!x.d || !y.d) { + if (!x.s || !y.s) { + y = new Decimal(NaN); + } else if (x.d) { + y.sign = -y.s; + } else { + y = new Decimal(y.d || x.s !== y.s ? x : NaN); + } + return y; + } + + if (x.s != y.s) { + y.sign = -y.s; + return x.add(y); + } + + let xd = x.d; + let yd = y.d; + let pr = Decimal.precision; + let rm = Decimal.rounding; + if (!xd![0] || !yd![0]) { + if (yd![0]) { + y.sign = -y.s; + } else if (xd![0]) { + y = new Decimal(x); + } else { + return new Decimal(rm === 3 ? -0 : 0); + } + return external ? this.finalise(y, pr, rm) : y; + } + + let e = Math.floor(y.e / LOG_BASE); + let xe = Math.floor(x.e / LOG_BASE); + xd = xd!.slice(); + let k = xe - e; + let len: number; + let i: number; + let xLTy: boolean; + if (k) { + let xLTy = k < 0; + let d: Array; + if (xLTy) { + d = xd!; + k = -k; + len = yd!.length; + } else { + d = yd!; + e = xe; + len = xd!.length; + } + let i = Math.max(Math.ceil(pr / LOG_BASE), len) + 2; + if (k > i) { + k = i; + d.length = 1; + } + d.reverse(); + for (i = k; i--;) { + d.push(0); + } + d.reverse(); + } else { + let i = xd!.length; + len = yd!.length; + xLTy = i < len; + if (xLTy) { + len = i; + } + for (i = 0; i < len; i++) { + if (xd![i] != yd![i]) { + xLTy = xd![i] < yd![i]; + break; + } + } + k = 0; + } + + if (xLTy) { + let d = xd; + xd = yd; + yd = d; + y.sign = -y.s; + } + len = xd!.length; + for (i = yd!.length - len; i > 0; --i) { + xd![len++] = 0; + } + let j: number; + for (i = yd!.length; i > k;) { + if (xd![--i] < yd![i]) { + for (j = i; j && xd![--j] === 0;) { + xd![j] = BASE - 1; + } + --xd![j]; + xd![i] = xd![i] + BASE; + } + xd![i] -= yd![i]; + } + + for (; xd![--len] === 0;) { + xd!.pop(); + } + + for (; xd![0] === 0; xd!.shift()) { + --e; + } + + if (!xd![0]) { + return new Decimal(rm === 3 ? -0 : 0); + } + y.digits = xd; + y.exponent = this.getBase10Exponent(xd!, e); + return external ? this.finalise(y, pr, rm) : y; + } + + public mul(n: DecimalValue): Decimal { + let xd = this.digits; + let y = new Decimal(n); + let yd = y.d; + y.sign *= this.sign; + if (!xd || !(xd![0]) || !yd || !(yd![0])) { + return new Decimal((!y.s || xd && !(xd![0]) && !yd || yd && !(yd![0]) && !xd) + ? NaN + : (!xd || !yd) ? y.s / 0 : y.s * 0); + } + let e = Math.floor(this.exponent / LOG_BASE) + Math.floor(y.e / LOG_BASE); + let xdL = xd!.length; + let ydL = yd!.length; + let r: Array | null; + let rL: number; + if (xdL < ydL) { + r = xd; + xd = yd; + yd = r; + rL = xdL; + xdL = ydL; + ydL = rL; + } + r = new Array(); + rL = xdL + ydL; + let i: number; + for (i = rL; i--;) { + r.push(0); + } + + let carry: number = 0; + let k: number; + let t: number; + for (i = ydL; --i >= 0;) { + carry = 0; + for (k = xdL + i; k > i;) { + t = r[k] + yd![i] * xd![k - i - 1] + carry; + r[k--] = t % BASE | 0; + carry = t / BASE | 0; + } + r[k] = (r[k] + carry) % BASE | 0; + } + + for (; !r[--rL];) { + r.pop(); + } + if (carry) { + ++e; + } else { + r.shift(); + } + y.digits = r; + y.exponent = this.getBase10Exponent(r, e); + return external ? this.finalise(y, Decimal.precision, Decimal.rounding) : y; + } + + private initializeByDecimal(v: Decimal) { + this.sign = v.s; + if (external) { + if (!v.d || v.e > Decimal.maxE) { + this.exponent = NaN; + this.digits = null; + } else if (v.e < Decimal.minE) { + this.exponent = 0; + this.digits = Utils.updateDigits(0); + } else { + this.exponent = v.e; + this.digits = v.d!.slice(); + } + } else { + this.exponent = v.e; + this.digits = v.d ? v.d!.slice() : v.d; + } + } + + private initializeByNumber(v: number) { + if (v === 0) { + this.sign = 1 / v < 0 ? -1 : 1; + this.exponent = 0; + this.digits = Utils.updateDigits(0); + return; + } + let tv = v; + if (v < 0) { + tv = -v; + this.sign = -1; + } else { + this.sign = 1; + } + + if (tv === ~~tv && tv < 1e7) { + let e: number; + let i: number; + for (e = 0, i = tv; i >= 10; i /= 10) { + e++; + } + if (external) { + if (e > Decimal.maxE) { + this.exponent = NaN; + this.digits = null; + } else if (e < Decimal.minE) { + this.exponent = 0; + this.digits = Utils.updateDigits(0); + } else { + this.exponent = e; + this.digits = Utils.updateDigits(tv); + } + } else { + this.exponent = e; + this.digits = Utils.updateDigits(tv); + } + return; + } else if (tv * 0 !== 0) { + if (!tv) { + this.sign = NaN; + } + this.exponent = NaN; + this.digits = null; + return; + } + this.parseDecimal(Utils.toString(tv)); + } + + private initializeByString(v: string) { + let i: number; + let str: string = v; + if ((i = v.charCodeAt(0)) === 45) { + str = v.slice(1); + this.sign = -1; + } else { + if (i === 43) { + str = v.slice(1); + } + this.sign = 1; + } + if (isDecimal.test(str)) { + this.parseDecimal(str); + } else { + this.parseOther(str); + } + } + + private parseDecimal(str: String): void { + let e: number; + let i: number; + let len: number; + if ((e = str.indexOf('.')) > -1) { + str = str.replace('.', ''); + } + + if ((i = str.search(new RegExp("e", "i"))) > 0) { + if (e < 0) { + e = i; + } + e += Utils.toNumber(str.slice(i + 1)); + str = str.substring(0, i); + } else if (e < 0) { + e = str.length; + } + + for (i = 0; str.charCodeAt(i) === 48; i++); + + for (len = str.length; str.charCodeAt(len - 1) === 48; --len); + str = str.slice(i, len); + if (str) { + len -= i; + this.exponent = e = e - i - 1; + this.digits = new Array(); + i = (e + 1) % LOG_BASE; + if (e < 0) { + i += LOG_BASE; + } + if (i < len) { + if (i) { + this.digits!.push(Utils.toNumber(str.slice(0, i))); + } + for (len -= LOG_BASE; i < len;) { + this.digits!.push(Utils.toNumber(str.slice(i, i += LOG_BASE))); + } + str = str.slice(i); + i = LOG_BASE - str.length; + } else { + i -= len; + } + + for (; i--;) { + str += '0'; + } + this.digits!.push(Utils.toNumber(str)); + if (external) { + if (this.exponent > Decimal.maxE) { + this.digits = null; + this.exponent = NaN; + } else if (this.exponent < Decimal.minE) { + this.exponent = 0; + this.digits = Utils.updateDigits(0); + } + } + } else { + this.exponent = 0; + this.digits = Utils.updateDigits(0); + } + return; + } + + private parseOther(str: string): void { + if (str.indexOf('_') > -1) { + str = str.replace(new RegExp("(\\d)_(?=\\d)", "g"), '$1'); + if (isDecimal.test(str)) { + this.parseDecimal(str); + return; + } + } else if (str === 'Infinity' || str === 'NaN') { + if (!Utils.toNumber(str)) { + this.sign = NaN; + } + this.exponent = NaN; + this.digits = null; + return; + } + + let base: number; + if (isHex.test(str)) { + base = 16; + str = str.toLowerCase(); + } else if (isBinary.test(str)) { + base = 2; + } else if (isOctal.test(str)) { + base = 8; + } else { + Utils.throwBusinessError( + `The type of "test(str)" must be Hex/Binary/Octal. Received value is: ${str}`, TYPE_ERROR_CODE); + } + + let i = str.search(new RegExp("p", "i")); + let p: number = 0; + if (i > 0) { + p = Utils.toNumber(str.slice(i + 1)); + str = str.substring(2, i); + } else { + str = str.slice(2); + } + + i = str.indexOf('.'); + let isFloat: boolean = i >= 0; + let len: number = 0; + let divisor: Decimal = new Decimal(0); + if (isFloat) { + str = str.replace('.', ''); + len = str.length; + i = len - i; + divisor = this.intPow(new Decimal(base), i, i * 2); + } + let xd = this.convertBase(str, base, BASE); + let xe = xd.length - 1; + for (i = xe; i >= 0 && xd[i] === 0; --i) { + xd.pop(); + } + if (i < 0) { + this.sign = this.sign < 0 ? -1 : 1; + this.exponent = 0; + this.digits = Utils.updateDigits(0); + return; + } + this.exponent = this.getBase10Exponent(xd, xe); + this.digits = xd; + external = false; + if (isFloat) { + let x = this.divide(this, divisor, len * 4); + this.initializeByDecimal(x); + } + + if (p) { + let x = this.mul(Math.abs(p) < 54 ? Math.pow(2, p) : Utils.pow(2, p)); + this.initializeByDecimal(x) + } + external = true; + return; + } + + private intPow(x: Decimal, n: number, pr: number): Decimal { + let r = new Decimal(1); + let k = Math.ceil(pr / LOG_BASE + 4); + external = false; + let isTruncated: boolean = false; + for (;;) { + if (n % 2) { + r = r.mul(x); + if (this.truncate(r.d!, k)) { + isTruncated = true; + } + } + + n = Math.floor(n / 2); + if (n === 0) { + n = r.d!.length - 1; + if (isTruncated && r.d![n] === 0) { + ++r.d![n]; + } + break; + } + x = x.mul(x); + this.truncate(x.d!, k); + } + external = true; + return r; + } + + private getBase10Exponent(digits: Array, e: number): number { + let w = digits[0]; + for (e *= LOG_BASE; w >= 10; w /= 10) { + e++; + } + return e; + } + + private finalise(x: Decimal, sd: number, rm: number | undefined, isTruncated: boolean = false): Decimal { + if (sd != null) { + let xd = x.d; + if (!xd) { + return x; + } + + let digits: number; + let k: number; + for (digits = 1, k = xd![0]; k >= 10; k /= 10) { + digits++; + } + let i = sd - digits; + let j: number; + let w: number; + let xdi: number = 0; + let rd: number; + if (i < 0) { + i += LOG_BASE; + j = sd; + w = xd[xdi]; + rd = w / Math.pow(10, digits - j - 1) % 10 | 0; + } else { + xdi = Math.ceil((i + 1) / LOG_BASE); + k = xd.length; + if (xdi >= k) { + if (isTruncated) { + for (; k++ <= xdi;) { + xd!.push(0); + } + w = rd = 0; + digits = 1; + i %= LOG_BASE; + j = i - LOG_BASE + 1; + } else { + return this.finaliseExternal(x); + } + } else { + w = k = xd[xdi]; + for (digits = 1; k >= 10; k /= 10) { + digits++; + } + + i %= LOG_BASE; + j = i - LOG_BASE + digits; + rd = j < 0 ? 0 : w / Math.pow(10, digits - j - 1) % 10 | 0; + } + } + + isTruncated = isTruncated || sd < 0 + || ((xdi + 1) < xd!.length && xd![xdi + 1] !== undefined) + || (j < 0 ? w : w % Math.pow(10, digits - j - 1)) > 0; + + let roundUp = (rm != undefined && rm! < 4) + ? (rd || isTruncated) && (rm == 0 || rm == (x.s < 0 ? 3 : 2)) + : rd > 5 || rd == 5 && (rm == 4 || isTruncated || rm == 6 && + ((i > 0 ? j > 0 ? w / Math.pow(10, digits - j) : 0 : xd![xdi - 1]) % 10) & 1 || + rm == (x.s < 0 ? 8 : 7)); + + if (sd < 1 || !(xd![0])) { + xd!.length = 0; + if (roundUp) { + sd -= x.e + 1; + xd!.push(Math.pow(10, (LOG_BASE - sd % LOG_BASE) % LOG_BASE)); + x.exponent = -sd || 0; + } else { + xd!.push(x.exponent = 0); + } + return x; + } + + if (i == 0) { + xd!.length = xdi; + k = 1; + xdi--; + } else { + xd!.length = xdi + 1; + k = Math.pow(10, LOG_BASE - i); + xd![xdi] = j > 0 ? (w / Math.pow(10, digits - j) % Math.pow(10, j) | 0) * k : 0; + } + + if (roundUp) { + for (;;) { + if (xdi == 0) { + for (i = 1, j = xd![0]; j >= 10; j /= 10) { + i++; + } + xd![0] = xd![0] + k; + j = xd![0]; + for (k = 1; j >= 10; j /= 10) { + k++; + } + if (i != k) { + x.exponent++; + if (xd![0] == BASE) { + xd![0] = 1; + } + } + break; + } else { + xd![xdi] = xd![xdi] + k; + if (xd![xdi] != BASE) { + break; + } + xd![xdi--] = 0; + k = 1; + } + } + } + for (i = xd.length; xd![--i] === 0;) { + xd!.pop(); + } + } + return this.finaliseExternal(x); + } + + private finaliseExternal(x: Decimal): Decimal { + if (external) { + if (x.e > Decimal.maxE) { + x.digits = null; + x.exponent = NaN; + } else if (x.e < Decimal.minE) { + x.exponent = 0; + x.digits = Utils.updateDigits(0); + } + } + return x; + } + + private truncate(arr: Array, len: number): boolean { + if (arr.length > len) { + arr.length = len; + return true; + } + return false; + } + + private convertBase(str: string, baseIn: number, baseOut: number): Array { + let arrL: number; + let arr: Array = Utils.updateDigits(0); + for (let i = 0; i < str.length;) { + for (arrL = arr.length; arrL--;) { + arr[arrL] = arr[arrL] * baseIn; + } + arr[0] = arr[0] + NUMERALS.indexOf(str.charAt(i++)); + for (let j = 0; j < arr.length; j++) { + if (arr[j] > baseOut - 1) { + if ((j + 1) == arr.length) { + arr.push(0); + } + arr[j + 1] = arr[j + 1] + (arr[j] / baseOut | 0); + arr[j] = arr[j] % baseOut; + } + } + } + return arr.reverse(); + } + + private finiteToString(isExp: boolean, sd?: number): string { + if (!this.isFinite()) { + return this.nonFiniteToString(); + } + let k: number; + let e = this.exponent; + let str: string = Utils.digitsToString(this.d!); + let len: number = str.length; + if (isExp) { + if (sd && (k = sd - len) > 0) { + str = str.charAt(0) + '.' + str.slice(1) + Utils.getZeroString(k); + } else if (len > 1) { + str = str.charAt(0) + '.' + str.slice(1); + } + str = str + (this.e < 0 ? 'e' : 'e+') + this.e; + } else if (e < 0) { + str = '0.' + Utils.getZeroString(-e - 1) + str; + if (sd && (k = sd - len) > 0) str += Utils.getZeroString(k); + } else if (e >= len) { + str += Utils.getZeroString(e + 1 - len); + if (sd && (k = sd - e - 1) > 0) str = str + '.' + Utils.getZeroString(k); + } else { + if ((k = e + 1) < len) str = str.slice(0, k) + '.' + str.slice(k); + if (sd && (k = sd - len) > 0) { + if (e + 1 === len) str += '.'; + str += Utils.getZeroString(k); + } + } + return str; + } + + private nonFiniteToString(): String { + return String(this.sign * this.sign / 0); + } + + private isNeg(): boolean { + return this.sign < 0; + } + + private divide(x: Decimal, y: Decimal, pr?: number, rm?: number, dp?: boolean, base?: number) { + let sign = x.s == y.s ? 1 : -1; + let xd = x.d; + let yd = y.d; + if (!xd || !xd![0] || !yd || !yd![0]) { + return new Decimal( + !x.s || !y.s || (xd ? yd && xd![0] == yd![0] : !yd) ? NaN : + xd && xd![0] == 0 || !yd ? sign * 0 : sign / 0); + } + let logBase: number; + let e: number; + if (base) { + logBase = 1; + e = x.e - y.e; + } else { + base = BASE; + logBase = LOG_BASE; + e = Math.floor(x.e / logBase) - Math.floor(y.e / logBase); + } + let yL = yd.length; + let xL = xd.length; + let q = new Decimal(sign); + let qd = new Array(); + q.digits = qd; + let i: number; + for (i = 0; i < yL && yd![i] == (xd![i] || 0); i++); + + if (yd![i] > (xd![i] || 0)) { + e--; + } + let sd: number; + if (pr == undefined) { + sd = pr = Decimal.precision; + rm = Decimal.rounding; + } else if (dp) { + sd = pr + (x.exponent - y.e) + 1; + } else { + sd = pr; + } + let more: boolean; + let k: number; + if (sd < 0) { + qd.push(1); + more = true; + } else { + sd = sd / logBase + 2 | 0; + i = 0; + if (yL == 1) { + k = 0; + let ydv = yd![0]; + sd++; + let t: number; + for (; (i < xL || k) && sd--; i++) { + t = k * base + (i < xd!.length ? (xd![i] || 0) : 0); + qd.push(t / ydv | 0); + k = t % ydv | 0; + } + more = (k || i) < xL; + } else { + k = base / (yd![0] + 1) | 0; + if (k > 1) { + yd = Utils.multiplyInteger(yd, k, base); + xd = Utils.multiplyInteger(xd, k, base); + yL = yd!.length; + xL = xd!.length; + } + let xi = yL; + let rem: Array | undefined = xd!.slice(0, yL); + let remL = rem.length; + for (; remL < yL;) { + if (remL == rem.length) { + rem.push(0); + remL++; + } else { + rem[remL++] = 0; + } + } + let yz = yd!.slice(); + yz.unshift(0); + let yd0 = yd![0]; + if (yd![1] >= base / 2) { + ++yd0; + } + do { + k = 0; + let cmp = Utils.compare(yd, rem!, yL, remL); + if (cmp < 0) { + let rem0 = rem![0]; + if (yL != remL) { + rem0 = rem0 * base + (rem![1] || 0); + } + k = rem0 / yd0 | 0; + let prod: Array; + if (k > 1) { + if (k >= base) { + k = base - 1; + } + prod = Utils.multiplyInteger(yd, k, base); + let prodL = prod.length; + remL = rem!.length; + cmp = Utils.compare(prod, rem!, prodL, remL); + if (cmp == 1) { + k--; + Utils.subtract(prod, yL < prodL ? yz : yd, prodL, base); + } + } else { + if (k == 0) { + cmp = k = 1; + } + prod = yd!.slice(); + } + let prodL = prod.length; + if (prodL < remL) { + prod.unshift(0); + } + Utils.subtract(rem!, prod, remL, base); + if (cmp == -1) { + remL = rem!.length; + cmp = Utils.compare(yd, rem!, yL, remL); + if (cmp < 1) { + k++; + Utils.subtract(rem!, yL < remL ? yz : yd, remL, base); + } + } + remL = rem!.length; + } else if (cmp === 0) { + k++; + rem = Utils.updateDigits(0); + } + qd.push(k); + i++; + if (cmp && rem![0]) { + rem!.push(xi < xL ? (xd![xi] || 0) : 0); + remL++; + } else { + rem = xi < xd!.length ? Utils.updateDigits(xd![xi]) : undefined; + remL = 1; + } + } while ((xi++ < xL || rem !== undefined) && sd--); + more = rem !== undefined; + } + if (!qd![0]) { + qd!.shift(); + } + } + if (logBase == 1) { + q.exponent = e; + } else { + for (i = 1, k = qd![0]; k >= 10; k /= 10) { + i++; + } + q.exponent = i + e * logBase - 1; + let sd: number = dp ? pr + q.e + 1 : pr; + this.finalise(q, sd, rm, more); + } + return q; + } + + private naturalExponential(x: Decimal, sd?: number): Decimal { + let rm = Decimal.rounding; + let pr = Decimal.precision; + if (!x.d || !x.d![0] || x.e > 17) { + return new Decimal(x.d + ? !x.d![0] ? 1 as number : x.s < 0 ? 0 as number : Infinity + : x.s ? x.s < 0 ? 0 as number : x : NaN); + } + + let wpr: number; + if (sd == null) { + external = false; + wpr = pr; + } else { + wpr = sd; + } + let t = new Decimal(0.03125); + let k: number = 0; + while (x.e > -2) { + x = x.mul(t); + k += 5; + } + + let guard = Math.log(Math.pow(2, k)) / Math.LN10 * 2 + 5 | 0; + wpr += guard; + let denominator = new Decimal(1); + let pow = new Decimal(1); + let sum = new Decimal(1); + Decimal.precision = wpr; + let i = 0; + for (;;) { + pow = this.finalise(pow.mul(x), wpr, 1); + denominator = denominator.mul(++i); + t = sum.add(this.divide(pow, denominator, wpr, 1)); + if (Utils.digitsToString(t.d!).slice(0, wpr) === Utils.digitsToString(sum.d!).slice(0, wpr)) { + let j = k; + while (j--) { + sum = this.finalise(sum.mul(sum), wpr, 1); + } + + if (sd == null) { + let rep: number = 0; + if (rep < 3 && Utils.checkRoundingDigits(sum.d!, wpr - guard, rm, rep)) { + Decimal.precision = wpr += 10; + denominator = pow = t = new Decimal(1); + i = 0; + rep++; + } else { + return this.finalise(sum, Decimal.precision = pr, rm, external = true); + } + } else { + Decimal.precision = pr; + return sum; + } + } + sum = t; + } + } + + private naturalLogarithm(y: Decimal, sd?: number): Decimal { + let x = y; + let xd = x.d; + let rm = Decimal.rounding; + let n: number = 1; + if (x.s < 0 || !xd || !xd![0] || !x.e && xd![0] == 1 && xd!.length == 1) { + return new Decimal(xd && !xd![0] ? -Infinity : x.s != 1 ? NaN : xd ? 0 as number : x); + } + let wpr: number; + let pr = Decimal.precision; + if (sd == null) { + external = false; + wpr = pr; + } else { + wpr = sd; + } + let guard: number = 10; + Decimal.precision = wpr += guard; + let c = Utils.digitsToString(xd!); + let c0 = Utils.toNumber(c.charAt(0)); + let e: number; + if (Math.abs(e = x.e) < 1.5e15) { + let n: number = 1; + while (c0 < 7 && c0 != 1 || c0 == 1 && (Utils.toNumber(c.charAt(1))) > 3) { + x = x.mul(y); + c = Utils.digitsToString(x.d!); + c0 = c.charAt(0); + n++; + } + e = x.e; + if (c0 > 1) { + x = new Decimal('0.' + c); + e++; + } else { + x = new Decimal(c0 + '.' + c.slice(1)); + } + } else { + let t = this.getLn10(wpr + 2, pr).mul(e + ''); + x = this.naturalLogarithm(new Decimal(c0 + '.' + c.slice(1)), wpr - guard).add(t); + Decimal.precision = pr; + return sd == null ? this.finalise(x, pr, rm, external = true) : x; + } + let x1 = x; + let numerator: Decimal; + let sum = numerator = x = this.divide(x.sub(1), x.add(1), wpr, 1); + let x2 = this.finalise(x.mul(x), wpr, 1); + let denominator: number = 3; + for (;;) { + numerator = this.finalise(numerator.mul(x2), wpr, 1); + let t = sum.add(this.divide(numerator, new Decimal(denominator), wpr, 1)); + if (Utils.digitsToString(t.d!).slice(0, wpr) === Utils.digitsToString(sum.d!).slice(0, wpr)) { + sum = sum.mul(2); + if (e !== 0) { + sum = sum.add(this.getLn10(wpr + 2, pr).mul(e + '')); + } + sum = this.divide(sum, new Decimal(n), wpr, 1); + let rep: number = 0; + if (sd == null) { + if (Utils.checkRoundingDigits(sum.d!, wpr - guard, rm, rep)) { + Decimal.precision = wpr += guard; + t = numerator = x = this.divide(x1.sub(1), x1.add(1), wpr, 1); + x2 = this.finalise(x.mul(x), wpr, 1); + denominator = rep = 1; + } else { + return this.finalise(sum, Decimal.precision = pr, rm, external = true); + } + } else { + Decimal.precision = pr; + return sum; + } + } + sum = t; + denominator += 2; + } + } + + private getLn10(sd: number, pr?: number): Decimal { + if (sd > LN10_PRECISION) { + external = true; + if (pr) { + Decimal.precision = pr; + } + Utils.throwBusinessError( + `Precision limit exceeded, "sd" must be <= LN10_PRECISION`, PRECISION_LIMIT_EXCEEDED_ERROR_CODE); + } + return this.finalise(new Decimal(LN10), sd, 1, true); + } +} + +class Utils { + public static throwBusinessError(message: string, code: number): void { + let err = new BusinessError(); + err.code = code; + err.message = message; + throw err; + } + + public static isDecimalInstance(obj: Object): boolean { + return obj instanceof Decimal + || (obj.hasOwnProperty('toStringTag') && (obj as Decimal).toStringTag === tag) || false; + } + + public static getZeroString(k: number): string { + let zs: string = ''; + for (; k--;) { + zs += '0'; + } + return zs; + } + + public static digitsToString(d: Array): string { + let indexOfLastWord: number = d.length - 1; + let str: string = ''; + let w: number = d[0]; + if (indexOfLastWord > 0) { + str += w; + let i: number; + let k: number; + let ws: string; + for (i = 1; i < indexOfLastWord; i++) { + ws = d[i] + ''; + k = LOG_BASE - ws.length; + if (k) { + str += Utils.getZeroString(k); + } + str += ws; + } + w = d[i]; + ws = w + ''; + k = LOG_BASE - ws.length; + if (k) { + str += Utils.getZeroString(k); + } + } else if (w === 0) { + return '0'; + } + for (; w % 10 === 0;) { + w /= 10; + } + return str + w; + } + + public static checkInt32(i: number, min: number, max: number): void { + if (i !== ~~i || i < min || i > max) { + Utils.throwBusinessError( + `The value of "${i}" is out of range. It must be >= ${min} && <= ${max} . Received value is: ${i}`, + RANGE_ERROR_CODE + ); + } + } + + public static multiplyInteger(x: Array, k: number, base: number): Array { + let temp: number = 0; + let carry: number = 0; + let i: number = x.length; + for (x = x.slice(); i--;) { + temp = x[i] * k + carry; + x[i] = temp % base | 0; + carry = temp / base | 0; + } + if (carry) { + x.unshift(carry); + } + return x; + } + + public static compare(a: Array, b: Array, aL: number, bL: number): number { + let r: number = 0; + if (aL != bL) { + r = aL > bL ? 1 : -1; + } else { + for (let i = r = 0; i < aL; i++) { + if (a[i] != b[i]) { + r = a[i] > b[i] ? 1 : -1; + break; + } + } + } + return r; + } + + public static subtract(a: Array, b: Array, aL: number, base: number): void { + let i: number = 0; + for (; aL--;) { + a[aL] = a[aL] - i; + i = a[aL] < b[aL] ? 1 : 0; + a[aL] = i * base + a[aL] - b[aL]; + } + for (; !a[0] && a.length > 1;) { + a.shift(); + } + } + + public static pow(x: number, y: number): Decimal { + return new Decimal(x).pow(y); + } + + public static checkRoundingDigits(d: Array, i: number, rm: number, repeating?: number): boolean { + let k: number; + for (k = d[0]; k >= 10; k /= 10) { + --i; + } + let di: number; + if (--i < 0) { + i += LOG_BASE; + di = 0; + } else { + di = Math.ceil((i + 1) / LOG_BASE); + i %= LOG_BASE; + } + k = Math.pow(10, LOG_BASE - i); + let rd = d[di] % k | 0; + let r: boolean = false; + if (repeating == null) { + if (i < 3) { + if (i == 0) { + rd = rd / 100 | 0; + } else if (i == 1) { + rd = rd / 10 | 0; + } + r = rm < 4 && rd == 99999 || rm > 3 && rd == 49999 || rd == 50000 || rd == 0; + } else { + r = (rm < 4 && rd + 1 == k || rm > 3 && rd + 1 == k / 2) && + (d[di + 1] / k / 100 | 0) == Math.pow(10, i - 2) - 1 || + (rd == k / 2 || rd == 0) && (d[di + 1] / k / 100 | 0) == 0; + } + } else { + if (i < 4) { + if (i == 0) { + rd = rd / 1000 | 0; + } else if (i == 1) { + rd = rd / 100 | 0; + } else if (i == 2) { + rd = rd / 10 | 0; + } + r = (repeating > 0 || rm < 4) && rd == 9999 || !repeating && rm > 3 && rd == 4999; + } else { + r = ((repeating > 0 || rm < 4) && rd + 1 == k || + (!repeating && rm > 3) && rd + 1 == k / 2) && + (d[di + 1] / k / 1000 | 0) == Math.pow(10, i - 3) - 1; + } + } + return r; + } + + public static updateDigits(v?: number): Array { + let res = new Array(); + if (v != undefined) { + res.push(v!); + } + return res; + } + + public static toNumber(s: string): number { + return +Number.parseFloat(s); + } + + public static toExponential(n: number): String { + return new Double(n).toExponential(); + } + + public static toString(n: number): String { + return Double.toString(n); + } +} \ No newline at end of file -- Gitee