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 3d0f694 commit dce464d
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 53 deletions.
116 changes: 86 additions & 30 deletions src/decimal128.mts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,27 @@ function validateConstructorData(x: Decimal128Constructor): void {
}
}

function convertExponentialNotationToDecimalNotation(
significand: string,
exponent: number
): string {
if (exponent >= 0) {
return significand + "0".repeat(exponent);
}

if (significand.length + exponent <= 0) {
return (
"0." + "0".repeat(0 - exponent - significand.length) + significand
);
}

return (
significand.substring(0, significand.length + exponent) +
"." +
significand.substring(significand.length + exponent)
);
}

function handleNan(s: string): Decimal128Constructor {
return {
significand: "",
Expand All @@ -242,24 +263,9 @@ function handleNan(s: string): Decimal128Constructor {
}
function handleExponentialNotation(s: string): Decimal128Constructor {
let [sg, exp] = s.match(/e/) ? s.split("e") : s.split("E");

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

if (exp.match(/^[+]/)) {
exp = exp.substring(1);
}

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

function handleDecimalNotation(s: string): Decimal128Constructor {
Expand Down Expand Up @@ -483,7 +489,7 @@ type Decimal128ConstructorOptions = {
};

const digitStrRegExp = /^-?[0-9]+(?:_?[0-9]+)*(?:[.][0-9](_?[0-9]+)*)?$/;
const exponentRegExp = /^-?[1-9][0-9]*[eE][-+]?[1-9][0-9]*$/;
const exponentRegExp = /^-?[0-9]+([.][0-9]+)*[eE][-+]?[0-9]+$/;
const nanRegExp = /^-?nan$/i;
const infRegExp = /^-?inf(inity)?$/i;

Expand Down Expand Up @@ -598,7 +604,12 @@ export class Decimal128 {
return prefix + "0." + "0".repeat(0 - exp - sg.length) + sg;
}

return prefix + sg.substring(0, sg.length + exp) + "." + sg.substring(sg.length + exp);
return (
prefix +
sg.substring(0, sg.length + exp) +
"." +
sg.substring(sg.length + exp)
);
}

/**
Expand Down Expand Up @@ -869,12 +880,9 @@ export class Decimal128 {
let resultRat = Rational.multiply(this.rat, x.rat);
let renderedRat = resultRat.toDecimalPlaces(MAX_SIGNIFICANT_DIGITS + 1);

return new Decimal128(
renderedRat,
{
exponent: this.exponent + x.exponent
}
);
return new Decimal128(renderedRat, {
exponent: this.exponent + x.exponent,
});
}

private isZero(): boolean {
Expand Down Expand Up @@ -923,11 +931,59 @@ export class Decimal128 {
return new Decimal128("-0");
}

return new Decimal128(
Rational.divide(this.rat, x.rat).toDecimalPlaces(
MAX_SIGNIFICANT_DIGITS + 1
)
if (this.isNegative) {
return this.negate().divide(x).negate();
}

if (x.isNegative) {
return this.divide(x.negate()).negate();
}

let adjust = 0;
let dividendCoefficient = this.significand;
let divisorCoefficient = x.significand;

while (BigInt(dividendCoefficient) < BigInt(divisorCoefficient)) {
dividendCoefficient = dividendCoefficient + "0";
adjust++;
}

while (
BigInt(dividendCoefficient) >=
BigInt(divisorCoefficient) * 10n
) {
divisorCoefficient += "0";
adjust--;
}

let resultCoefficient = 0n;
let done = false;

while (!done) {
while (BigInt(divisorCoefficient) <= BigInt(dividendCoefficient)) {
dividendCoefficient = String(
BigInt(dividendCoefficient) - BigInt(divisorCoefficient)
);
resultCoefficient++;
}
if (
(BigInt(dividendCoefficient) === 0n && adjust >= 0) ||
resultCoefficient.toString().length > MAX_SIGNIFICANT_DIGITS
) {
done = true;
} else {
resultCoefficient = resultCoefficient * 10n;
dividendCoefficient = dividendCoefficient + "0";
adjust++;
}
}

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

/**
Expand Down
6 changes: 0 additions & 6 deletions tests/constructor.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,15 +238,9 @@ describe("constructor", () => {
expect(d.isNegative).toStrictEqual(false);
});
});
test("leading zero does not work", () => {
expect(() => new Decimal128("0123E10")).toThrow(SyntaxError);
});
test("nonsense string input", () => {
expect(() => new Decimal128("howdy")).toThrow(SyntaxError);
});
test("leading zero in exponent does not work", () => {
expect(() => new Decimal128("123E05")).toThrow(SyntaxError);
});
test("whitespace plus number not OK", () => {
expect(() => new Decimal128(" 42E10")).toThrow(SyntaxError);
});
Expand Down
56 changes: 56 additions & 0 deletions tests/divide.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,59 @@ describe("division", () => {
});
});
});

describe("examples from the General Decimal Arithmetic Specification", () => {
// some examples have been tweaked because we are working with more precision in Decimal128
test("example one", () => {
expect(
new Decimal128("1").divide(new Decimal128("3")).toString()
).toStrictEqual("0.3333333333333333333333333333333333");
});
test("example two", () => {
expect(
new Decimal128("2").divide(new Decimal128("3")).toString()
).toStrictEqual("0.6666666666666666666666666666666667");
});
test("example three", () => {
expect(
new Decimal128("5").divide(new Decimal128("2")).toString()
).toStrictEqual("2.5");
});
test("example four", () => {
expect(
new Decimal128("1").divide(new Decimal128("10")).toString()
).toStrictEqual("0.1");
});
test("example five", () => {
expect(
new Decimal128("12").divide(new Decimal128("12")).toString()
).toStrictEqual("1");
});
test("example six", () => {
expect(
new Decimal128("8.00").divide(new Decimal128("2")).toString()
).toStrictEqual("4.00");
});
test("example seven", () => {
expect(
new Decimal128("2.400").divide(new Decimal128("2.0")).toString()
).toStrictEqual("1.20");
});
test("example eight", () => {
expect(
new Decimal128("1000").divide(new Decimal128("100")).toString()
).toStrictEqual("10");
});
test("example nine", () => {
expect(
new Decimal128("1000").divide(new Decimal128("1")).toString()
).toStrictEqual("1000");
});
test("example ten", () => {
expect(
new Decimal128("2.40E+6")
.divide(new Decimal128("2"))
.toExponentialString()
).toStrictEqual("1.20E6");
});
});
43 changes: 28 additions & 15 deletions tests/multiply.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,19 +158,32 @@ describe("multiplication", () => {
});

describe("examples from the General Decimal Arithmetic specification", () => {
test("example one", () => {
expect(new Decimal128("1.20").multiply(new Decimal128("3")).toString()).toStrictEqual("3.60");
});
test("example two", () => {
expect(new Decimal128("7").multiply(new Decimal128("3")).toString()).toStrictEqual("21");
});
test("example three", () => {
expect(new Decimal128("0.9").multiply(new Decimal128("0.8")).toString()).toStrictEqual("0.72");
});
test("example four", () => {
expect(new Decimal128("0.9").multiply(new Decimal128("-0")).toString()).toStrictEqual("-0.0");
});
test("example five", () => { // slightly modified because we have more precision
expect(new Decimal128("654321").multiply(new Decimal128("654321")).toExponentialString()).toStrictEqual("4.28135971041E11");
});
test("example one", () => {
expect(
new Decimal128("1.20").multiply(new Decimal128("3")).toString()
).toStrictEqual("3.60");
});
test("example two", () => {
expect(
new Decimal128("7").multiply(new Decimal128("3")).toString()
).toStrictEqual("21");
});
test("example three", () => {
expect(
new Decimal128("0.9").multiply(new Decimal128("0.8")).toString()
).toStrictEqual("0.72");
});
test("example four", () => {
expect(
new Decimal128("0.9").multiply(new Decimal128("-0")).toString()
).toStrictEqual("-0.0");
});
test("example five", () => {
// slightly modified because we have more precision
expect(
new Decimal128("654321")
.multiply(new Decimal128("654321"))
.toExponentialString()
).toStrictEqual("4.28135971041E11");
});
});
8 changes: 6 additions & 2 deletions tests/pow.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ describe("pow", () => {
).toStrictEqual("1");
});
test("negative base", () => {
expect(new Decimal128("-2").pow(new Decimal128("3")).toString()).toStrictEqual("-8");
expect(
new Decimal128("-2").pow(new Decimal128("3")).toString()
).toStrictEqual("-8");
});
test("negative power", () => {
expect(
Expand All @@ -25,7 +27,9 @@ describe("pow", () => {
).toStrictEqual("967173.11574016");
});
test("non-integer base", () => {
expect(new Decimal128("1.7").pow(new Decimal128("8")).toString()).toStrictEqual("69.75757441");
expect(
new Decimal128("1.7").pow(new Decimal128("8")).toString()
).toStrictEqual("69.75757441");
});
describe("infinity", () => {
test("positive infinity", () => {
Expand Down

0 comments on commit dce464d

Please sign in to comment.