Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
jessealama committed Dec 8, 2023
1 parent dce464d commit c0308fd
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 20 deletions.
6 changes: 2 additions & 4 deletions src/common.mts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@ export function countSignificantDigits(s: string): number {
return s.length - 1;
}

let m = s.match(/0+$/);
let m = s.match(/^0+$/);

if (m) {
return s.length - m[0].length;
}
s = s.replace(/&0+$/, "");

return s.length;
}
Expand Down
104 changes: 88 additions & 16 deletions src/decimal128.mts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,11 @@ function cutoffAfterSignificantDigits(s: string, n: number): string {
return s.substring(0, n + 2);
}

return s.substring(0, n + 1);
if (s.match(/[.]/)) {
return s.substring(0, n);
}

return s.substring(0, n);
}

function propagateCarryFromRight(s: string): string {
Expand Down Expand Up @@ -207,19 +211,15 @@ interface Decimal128Constructor {
isNegative: boolean;
}

function isInteger(x: Decimal128Constructor): boolean {
return !x.isNaN && x.isFinite && x.exponent >= bigZero;
}

function validateConstructorData(x: Decimal128Constructor): void {
if (x.isNaN) {
return; // no further validation needed
}

let numSigDigits = countSignificantDigits(x.significand);

if (isInteger(x) && numSigDigits > MAX_SIGNIFICANT_DIGITS) {
throw new RangeError("Integer too large");
if (numSigDigits > MAX_SIGNIFICANT_DIGITS) {
throw new RangeError("Too many significant digits");
}

if (x.exponent > EXPONENT_MAX) {
Expand Down Expand Up @@ -262,10 +262,86 @@ function handleNan(s: string): Decimal128Constructor {
};
}
function handleExponentialNotation(s: string): Decimal128Constructor {
let [sg, exp] = s.match(/e/) ? s.split("e") : s.split("E");
return handleDecimalNotation(
convertExponentialNotationToDecimalNotation(sg, Number(exp))
);
let [sg, expString] = s.match(/e/) ? s.split("e") : s.split("E");
let isNegative = false;
let exp = Number(expString);

if (sg.match(/^-/)) {
isNegative = true;
sg = sg.substring(1);
}

if (sg.match(/[.]/)) {
let [lhs, rhs] = sg.split(".");
sg = lhs + rhs;
exp = exp - rhs.length;
}

let numSigDigits = sg.length;

if (numSigDigits > MAX_SIGNIFICANT_DIGITS) {
let lastDigit = parseInt(sg.charAt(MAX_SIGNIFICANT_DIGITS));
let penultimateDigit = parseInt(sg.charAt(MAX_SIGNIFICANT_DIGITS - 1));
let excessDigits = sg.substring(MAX_SIGNIFICANT_DIGITS);
let numExcessDigits = excessDigits.length;
exp = exp + numExcessDigits; // we will chop off the excess digits
if (lastDigit === 5) {
if (penultimateDigit % 2 === 0) {
let rounded = cutoffAfterSignificantDigits(
sg,
MAX_SIGNIFICANT_DIGITS - 1
);
sg = significand(rounded);
} else if (9 === penultimateDigit) {
let rounded =
cutoffAfterSignificantDigits(
propagateCarryFromRight(
cutoffAfterSignificantDigits(
sg,
MAX_SIGNIFICANT_DIGITS - 1
)
),
MAX_SIGNIFICANT_DIGITS - 2
) + "0";
sg = significand(rounded);
} else {
let rounded =
cutoffAfterSignificantDigits(
sg,
MAX_SIGNIFICANT_DIGITS - 2
) + `${penultimateDigit + 1}`;
sg = significand(rounded);
}
} else if (lastDigit > 5) {
if (9 === penultimateDigit) {
let rounded = propagateCarryFromRight(
cutoffAfterSignificantDigits(sg, MAX_SIGNIFICANT_DIGITS - 1)
);
sg = significand(rounded);
} else {
let cutoff = cutoffAfterSignificantDigits(
sg,
MAX_SIGNIFICANT_DIGITS
);
let rounded = propagateCarryFromRight(cutoff);
sg = significand(rounded);
}
} else {
let rounded = cutoffAfterSignificantDigits(
sg,
MAX_SIGNIFICANT_DIGITS
);
sg = significand(rounded);
}
}

return {
significand: sg,
exponent: Number(exp),
isNegative: isNegative,
isNaN: false,
isFinite: true,
};
}

function handleDecimalNotation(s: string): Decimal128Constructor {
Expand Down Expand Up @@ -979,11 +1055,7 @@ export class Decimal128 {
}

let resultExponent = this.exponent - (x.exponent + adjust);
let resultDigits = convertExponentialNotationToDecimalNotation(
resultCoefficient.toString(),
resultExponent
);
return new Decimal128(resultDigits);
return new Decimal128(`${resultCoefficient}E${resultExponent}`);
}

/**
Expand Down
16 changes: 16 additions & 0 deletions tests/constructor.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@ describe("constructor", () => {
test("no normalization", () => {
expect(new Decimal128("1.20").toString()).toStrictEqual("1.20");
});
test("no normalization (exponential notation) (positive exponent)", () => {
let d = new Decimal128("1.20E1");
expect(d.significand).toStrictEqual("120");
expect(d.exponent).toStrictEqual(-1);
});
test("no normalization (exponential notation) (negative exponent)", () => {
let d = new Decimal128("1.20E-5");
expect(d.significand).toStrictEqual("120");
expect(d.exponent).toStrictEqual(-7);
});
test("no normalization (exponential notation) (negative)", () => {
let d = new Decimal128("-42.79E42");
expect(d.significand).toStrictEqual("4279");
expect(d.exponent).toStrictEqual(40);
expect(d.isNegative).toStrictEqual(true);
});
test("string with underscores in integer part", () => {
expect(new Decimal128("123_456.789").toString()).toStrictEqual(
"123456.789"
Expand Down

0 comments on commit c0308fd

Please sign in to comment.