@abaplint/runtime
Version:
Transpiler - Runtime
145 lines • 4.77 kB
JavaScript
"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