UNPKG

@abaplint/runtime

Version:
145 lines 4.77 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Packed = void 0; const float_1 = require("./float"); const throw_error_1 = require("../throw_error"); const integer8_1 = require("./integer8"); const decfloat34_1 = require("./decfloat34"); const digits = new RegExp(/^\s*-?\+?\d*\.?\d*(?:E[+-]?\d+)? *$/i); function pow10(exp) { return 10n ** BigInt(exp); } // rescale a non-negative magnitude from fromScale to toScale decimal places, // rounding half away from zero function rescale(mag, fromScale, toScale) { if (toScale >= fromScale) { return mag * pow10(toScale - fromScale); } const div = pow10(fromScale - toScale); const q = mag / div; const r = mag % div; return r * 2n >= div ? q + 1n : q; } // format a non-negative magnitude at the given scale as a decimal string function formatMag(mag, scale) { let s = mag.toString(); if (scale === 0) { return s; } while (s.length <= scale) { s = "0" + s; } const intPart = s.slice(0, s.length - scale); const fracPart = s.slice(s.length - scale); return intPart + "." + fracPart; } class Packed { // value holds the number scaled by 10^decimals, as a bigint, to keep full precision value; length; decimals; qualifiedName; constructor(input) { this.value = 0n; this.length = 666; if (input?.length) { this.length = input.length; } this.decimals = 0; if (input?.decimals) { this.decimals = input.decimals; } this.qualifiedName = input?.qualifiedName; } clone() { const n = new Packed({ length: this.length, decimals: this.decimals, qualifiedName: this.qualifiedName }); n.value = this.value; return n; } getQualifiedName() { return this.qualifiedName; } numberToScaled(value) { const factor = Math.pow(10, this.decimals); return BigInt(Math.round(value * factor)); } stringToScaled(input) { let str = input.trim(); let negative = false; while (str.length > 0 && (str[0] === "+" || str[0] === "-")) { if (str[0] === "-") { negative = true; } str = str.slice(1); } if (/[eE]/.test(str)) { // scientific notation, fall back to floating point parsing return this.numberToScaled(parseFloat((negative ? "-" : "") + str)); } let intPart = str; let fracPart = ""; const dot = str.indexOf("."); if (dot >= 0) { intPart = str.slice(0, dot); fracPart = str.slice(dot + 1); } if (intPart === "") { intPart = "0"; } const kept = fracPart.slice(0, this.decimals).padEnd(this.decimals, "0"); let scaled = BigInt(intPart + kept); // round half up based on the first dropped fractional digit if (fracPart.length > this.decimals && fracPart[this.decimals] >= "5") { scaled += 1n; } return negative ? -scaled : scaled; } set(value) { if (typeof value === "number") { this.value = this.numberToScaled(value); } else if (typeof value === "string") { if (value.trim().length === 0) { this.value = 0n; return this; } else if (digits.test(value) === false) { (0, throw_error_1.throwError)("CX_SY_CONVERSION_NO_NUMBER"); } this.value = this.stringToScaled(value); } else if (value instanceof integer8_1.Integer8) { this.value = value.get() * pow10(this.decimals); } else if (value instanceof float_1.Float || value instanceof decfloat34_1.DecFloat34) { this.value = this.numberToScaled(value.getRaw()); } else { this.set(value.get()); } return this; } getLength() { return this.length; } getDecimals() { return this.decimals; } // returns the value as a fixed decimal string with the given number of // decimals, keeping full precision (unlike get() which is limited to double) toFixed(decimals) { const negative = this.value < 0n; const mag = negative ? -this.value : this.value; const scaled = rescale(mag, this.decimals, decimals); const formatted = formatMag(scaled, decimals); return (negative ? "-" : "") + formatted; } clear() { this.value = 0n; } get() { return Number(this.value) / Math.pow(10, this.decimals); } } exports.Packed = Packed; //# sourceMappingURL=packed.js.map