Skip to content
This repository was archived by the owner on Jan 15, 2025. It is now read-only.

Commit f18f0a7

Browse files
committed
WIP
1 parent 569e881 commit f18f0a7

File tree

4 files changed

+115
-109
lines changed

4 files changed

+115
-109
lines changed

src/Decimal.mts

+12
Original file line numberDiff line numberDiff line change
@@ -211,4 +211,16 @@ export class Decimal {
211211

212212
return e;
213213
}
214+
215+
public coefficient(): bigint {
216+
let sig = this.significand();
217+
let exp = this.exponent();
218+
let c = sig.scale10(exp);
219+
220+
if (!c.isInteger()) {
221+
throw new TypeError("The coefficient is not an integer.");
222+
}
223+
224+
return c.numerator;
225+
}
214226
}

src/Decimal128.mts

+59-17
Original file line numberDiff line numberDiff line change
@@ -344,22 +344,60 @@ export class Decimal128 {
344344

345345
private emitDecimal(): string {
346346
let v = this.cohort();
347+
let q = this.quantum();
347348

348349
if (v === "0") {
350+
if (q < 0) {
351+
return "0" + "." + "0".repeat(0 - q);
352+
}
353+
349354
return "0";
350355
}
351356

352357
if (v === "-0") {
358+
if (q < 0) {
359+
return "-0" + "." + "0".repeat(0 - q);
360+
}
361+
353362
return "-0";
354363
}
355364

356-
return v.toPrecision(MAX_SIGNIFICANT_DIGITS);
365+
let c = v.scale10(0 - q);
366+
367+
if (!c.isInteger()) {
368+
throw new TypeError("The coefficient is not an integer.");
369+
}
370+
371+
let s = c.numerator.toString();
372+
let p = this._isNegative ? "-" : "";
373+
374+
if (q > 0) {
375+
return p + s + "0".repeat(q);
376+
}
377+
378+
if (q === 0) {
379+
return p + s;
380+
}
381+
382+
if (s.length < Math.abs(q)) {
383+
let numZeroesNeeded = Math.abs(q) - s.length;
384+
return p + "0." + "0".repeat(numZeroesNeeded) + s;
385+
}
386+
387+
let integerPart = s.substring(0, s.length + q);
388+
let fractionalPart = s.substring(s.length + q);
389+
390+
if (integerPart === "") {
391+
integerPart = "0";
392+
}
393+
394+
return p + integerPart + "." + fractionalPart;
357395
}
358396

359397
/**
360398
* Returns a digit string representing this Decimal128.
361399
*/
362-
toString(opts?: { format?: "decimal" | "exponential" }): string {
400+
toString(opts?: { preserveTrailingZeroes?: boolean }): string {
363401
if (this.isNaN()) {
364402
return NAN;
365403
}
@@ -368,25 +406,29 @@ export class Decimal128 {
368406
return (this.isNegative() ? "-" : "") + POSITIVE_INFINITY;
369407
}
370408

371-
let asDecimalString = this.emitDecimal();
372-
let format = undefined;
409+
let preserveTrailingZeroes = false;
373410

374-
if ("object" === typeof opts && "string" === typeof opts.format) {
375-
if (opts.format === "exponential") {
376-
format = "exponential";
377-
} else if (opts.format === "decimal") {
378-
format = "decimal";
379-
} else {
380-
throw new TypeError(`Invalid toString format "${opts.format}"`);
381-
}
411+
if (
412+
"object" === typeof opts &&
413+
"boolean" === typeof opts.preserveTrailingZeroes
414+
) {
415+
preserveTrailingZeroes = opts.preserveTrailingZeroes;
382416
}
383417

384-
if (format === "exponential") {
385-
return this.emitExponential();
386-
}
418+
let asDecimalString = this.emitDecimal();
387419

388-
if (format === undefined && asDecimalString.length > 20) {
389-
return this.emitExponential();
420+
if (!preserveTrailingZeroes && asDecimalString.match(/[.]/)) {
421+
asDecimalString = asDecimalString.replace(/0+$/, "");
422+
if (asDecimalString === "") {
423+
asDecimalString = "0";
424+
} else if (asDecimalString === "-") {
425+
asDecimalString = "-0";
426+
} else if (asDecimalString.match(/[.]$/)) {
427+
asDecimalString = asDecimalString.substring(
428+
0,
429+
asDecimalString.length - 1
430+
);
431+
}
390432
}
391433

392434
return asDecimalString;

tests/Decimal128/constructor.test.js

+43-91
Original file line numberDiff line numberDiff line change
@@ -43,37 +43,37 @@ describe("constructor", () => {
4343
});
4444
test("five as last digit past limit: tie to even unchanged", () => {
4545
expect(
46-
new Decimal128("1234567890123456789012345678901234.5").toString(
47-
{ format: "decimal" }
48-
)
46+
new Decimal128(
47+
"1234567890123456789012345678901234.5"
48+
).toString()
4949
).toStrictEqual("1234567890123456789012345678901234");
5050
});
5151
test("five as last digit past limit: tie to even round up", () => {
5252
expect(
53-
new Decimal128("1234567890123456789012345678901235.5").toString(
54-
{ format: "decimal" }
55-
)
53+
new Decimal128(
54+
"1234567890123456789012345678901235.5"
55+
).toString()
5656
).toStrictEqual("1234567890123456789012345678901236");
5757
});
5858
test("five as last digit past limit: tie to even round up, penultimate digit is 9", () => {
5959
expect(
60-
new Decimal128("1234567890123456789012345678901239.5").toString(
61-
{ format: "decimal" }
62-
)
60+
new Decimal128(
61+
"1234567890123456789012345678901239.5"
62+
).toString()
6363
).toStrictEqual("1234567890123456789012345678901240");
6464
});
6565
test("five as last digit past limit: tie to even round up, penultimate digit is 9 (negative)", () => {
6666
expect(
6767
new Decimal128(
6868
"-1234567890123456789012345678901239.5"
69-
).toString({ format: "decimal" })
69+
).toString()
7070
).toStrictEqual("-1234567890123456789012345678901240");
7171
});
7272
test("round up decimal digit is not nine", () => {
7373
expect(
74-
new Decimal128("1234567890123456789012345678901239.8").toString(
75-
{ format: "decimal" }
76-
)
74+
new Decimal128(
75+
"1234567890123456789012345678901239.8"
76+
).toString()
7777
).toStrictEqual("1234567890123456789012345678901240");
7878
});
7979
test("empty string not OK", () => {
@@ -110,35 +110,29 @@ describe("constructor", () => {
110110
expect(
111111
new Decimal128(
112112
"0.3666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667"
113-
).toString({ format: "decimal" })
113+
).toString()
114114
).toStrictEqual("0.3666666666666666666666666666666667");
115115
});
116116
test("close to one, too many digits, gets rounded to 1.000...", () => {
117117
expect(
118-
new Decimal128("0." + "9".repeat(100)).toString({
119-
format: "decimal",
120-
})
118+
new Decimal128("0." + "9".repeat(100)).toString()
121119
).toStrictEqual("1");
122120
});
123121
test("lots of digits gets rounded to minus 1", () => {
124122
expect(
125-
new Decimal128("-0." + "9".repeat(100)).toString({
126-
format: "decimal",
127-
})
123+
new Decimal128("-0." + "9".repeat(100)).toString()
128124
).toStrictEqual("-1");
129125
});
130126
test("lots of digits gets rounded to 10", () => {
131127
expect(
132-
new Decimal128("9." + "9".repeat(100)).toString({
133-
format: "decimal",
134-
})
128+
new Decimal128("9." + "9".repeat(100)).toString()
135129
).toStrictEqual("10");
136130
});
137131
test("rounding at the limit of significant digits", () => {
138132
expect(
139133
new Decimal128(
140134
"0." + "1".repeat(MAX_SIGNIFICANT_DIGITS) + "9"
141-
).toString({ format: "decimal" })
135+
).toString()
142136
).toStrictEqual(
143137
"0." + "1".repeat(MAX_SIGNIFICANT_DIGITS - 1) + "2"
144138
);
@@ -147,46 +141,32 @@ describe("constructor", () => {
147141
expect(
148142
new Decimal128(
149143
"0." + "1".repeat(MAX_SIGNIFICANT_DIGITS + 100) + "9"
150-
).toString({ format: "decimal" })
144+
).toString()
151145
).toStrictEqual("0." + "1".repeat(MAX_SIGNIFICANT_DIGITS));
152146
});
153147
test("minus zero", () => {
154148
let minusZero = new Decimal128("-0");
155-
expect(minusZero.toString({ format: "decimal" })).toStrictEqual(
156-
"-0"
157-
);
149+
expect(minusZero.toString()).toStrictEqual("-0");
158150
expect(minusZero.isNegative()).toStrictEqual(true);
159151
});
160152
describe("zeros", () => {
161153
test("leading zeros get stripped", () => {
162-
expect(
163-
new Decimal128("00").toString({ format: "decimal" })
164-
).toStrictEqual("0");
154+
expect(new Decimal128("00").toString()).toStrictEqual("0");
165155
});
166156
test("leading zeros get stripped (negative)", () => {
167-
expect(
168-
new Decimal128("-00").toString({ format: "decimal" })
169-
).toStrictEqual("-0");
157+
expect(new Decimal128("-00").toString()).toStrictEqual("-0");
170158
});
171159
test("zero point zero", () => {
172-
expect(
173-
new Decimal128("0.0").toString({ format: "decimal" })
174-
).toStrictEqual("0");
160+
expect(new Decimal128("0.0").toString()).toStrictEqual("0");
175161
});
176162
test("minus zero point zero", () => {
177-
expect(
178-
new Decimal128("-0.0").toString({ format: "decimal" })
179-
).toStrictEqual("-0");
163+
expect(new Decimal128("-0.0").toString()).toStrictEqual("-0");
180164
});
181165
test("multiple trailing zeros", () => {
182-
expect(
183-
new Decimal128("0.000").toString({ format: "decimal" })
184-
).toStrictEqual("0");
166+
expect(new Decimal128("0.000").toString()).toStrictEqual("0");
185167
});
186168
test("multiple trailing zeros (negative)", () => {
187-
expect(
188-
new Decimal128("-0.000").toString({ format: "decimal" })
189-
).toStrictEqual("-0");
169+
expect(new Decimal128("-0.000").toString()).toStrictEqual("-0");
190170
});
191171
});
192172
});
@@ -285,44 +265,30 @@ describe("infinity", () => {
285265
describe("General Decimal Arithmetic specification", () => {
286266
describe("decimal syntax", () => {
287267
test("0", () => {
288-
expect(
289-
new Decimal128("0").toString({ format: "decimal" })
290-
).toStrictEqual("0");
268+
expect(new Decimal128("0").toString()).toStrictEqual("0");
291269
});
292270
test("12", () => {
293-
expect(
294-
new Decimal128("12").toString({ format: "decimal" })
295-
).toStrictEqual("12");
271+
expect(new Decimal128("12").toString()).toStrictEqual("12");
296272
});
297273
test("-76", () => {
298-
expect(
299-
new Decimal128("-76").toString({ format: "decimal" })
300-
).toStrictEqual("-76");
274+
expect(new Decimal128("-76").toString()).toStrictEqual("-76");
301275
});
302276
test("12.70", () => {
303-
expect(
304-
new Decimal128("12.70").toString({ format: "decimal" })
305-
).toStrictEqual("12.7");
277+
expect(new Decimal128("12.70").toString()).toStrictEqual("12.7");
306278
});
307279
test("+0.003", () => {
308-
expect(
309-
new Decimal128("+0.003").toString({ format: "decimal" })
310-
).toStrictEqual("0.003");
280+
expect(new Decimal128("+0.003").toString()).toStrictEqual("0.003");
311281
});
312282
test("017.", () => {
313-
expect(
314-
new Decimal128("017.").toString({ format: "decimal" })
315-
).toStrictEqual("17");
283+
expect(new Decimal128("017.").toString()).toStrictEqual("17");
316284
});
317285
test(".5", () => {
318-
expect(
319-
new Decimal128(".5").toString({ format: "decimal" })
320-
).toStrictEqual("0.5");
286+
expect(new Decimal128(".5").toString()).toStrictEqual("0.5");
321287
});
322288
test("4E+9", () => {
323-
expect(
324-
new Decimal128("4E+9").toString({ format: "decimal" })
325-
).toStrictEqual("4000000000");
289+
expect(new Decimal128("4E+9").toString()).toStrictEqual(
290+
"4000000000"
291+
);
326292
});
327293
test("Inf", () => {
328294
expect(() => new Decimal128("Inf")).toThrow(SyntaxError);
@@ -331,9 +297,7 @@ describe("General Decimal Arithmetic specification", () => {
331297
expect(() => new Decimal128("-infinity")).toThrow(SyntaxError);
332298
});
333299
test("NaN", () => {
334-
expect(
335-
new Decimal128("NaN").toString({ format: "decimal" })
336-
).toStrictEqual("NaN");
300+
expect(new Decimal128("NaN").toString()).toStrictEqual("NaN");
337301
});
338302
test("NaN8275 (diagnostic information discarded)", () => {
339303
expect(() => new Decimal128("NaN8275")).toThrow(SyntaxError);
@@ -358,39 +322,27 @@ describe("General Decimal Arithmetic specification", () => {
358322

359323
describe("number arguments", () => {
360324
test("integer", () => {
361-
expect(
362-
new Decimal128(42).toString({ format: "decimal" })
363-
).toStrictEqual("42");
325+
expect(new Decimal128(42).toString()).toStrictEqual("42");
364326
});
365327
test("non-integer number", () => {
366-
expect(
367-
new Decimal128(42.5).toString({ format: "decimal" })
368-
).toStrictEqual("42.5");
328+
expect(new Decimal128(42.5).toString()).toStrictEqual("42.5");
369329
});
370330
test("NaN", () => {
371-
expect(
372-
new Decimal128(NaN).toString({ format: "decimal" })
373-
).toStrictEqual("NaN");
331+
expect(new Decimal128(NaN).toString()).toStrictEqual("NaN");
374332
});
375333
test("minus zero", () => {
376-
expect(
377-
new Decimal128(-0).toString({ format: "decimal" })
378-
).toStrictEqual("-0");
334+
expect(new Decimal128(-0).toString()).toStrictEqual("-0");
379335
});
380336
test("very large value gets approximated", () => {
381337
expect(
382-
new Decimal128(123456789012345678901234567890123456789).toString({
383-
format: "decimal",
384-
})
338+
new Decimal128(123456789012345678901234567890123456789).toString()
385339
).toStrictEqual("123456789012345680000000000000000000000");
386340
});
387341
});
388342

389343
describe("bigint", () => {
390344
test("simple", () => {
391-
expect(
392-
new Decimal128(42n).toString({ format: "decimal" })
393-
).toStrictEqual("42");
345+
expect(new Decimal128(42n).toString()).toStrictEqual("42");
394346
});
395347
test("too big", () => {
396348
expect(

tests/common/common.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { countSignificantDigits } from "../src/common.mjs";
1+
import { countSignificantDigits } from "../../src/common.mjs";
22

33
describe("significant digits", () => {
44
test("basic example", () => {

0 commit comments

Comments
 (0)