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

Commit 47de179

Browse files
committed
Improve coverage
1 parent a3cb21e commit 47de179

14 files changed

+342
-231
lines changed

src/Decimal128.mts

+15-96
Original file line numberDiff line numberDiff line change
@@ -224,22 +224,13 @@ export class Decimal128 {
224224
}
225225

226226
private cohort(): "0" | "-0" | Rational {
227-
let d = this.d;
228-
if (d instanceof Decimal) {
229-
return d.cohort;
230-
}
231-
232-
throw new TypeError("Cannot compute cohort of a non-finite number");
227+
let d = this.d as Decimal;
228+
return d.cohort;
233229
}
234230

235231
private quantum(): number {
236-
let d = this.d;
237-
238-
if (d instanceof Decimal) {
239-
return d.quantum;
240-
}
241-
242-
throw new TypeError("Cannot compute quantum of a non-finite number");
232+
let d = this.d as Decimal;
233+
return d.quantum;
243234
}
244235

245236
private isZero(): boolean {
@@ -256,38 +247,6 @@ export class Decimal128 {
256247
return v === "0" || v === "-0";
257248
}
258249

259-
private significandAndExponent(): [Rational, number] {
260-
if (!this.isFinite()) {
261-
throw new RangeError("Infinity does not have a significand");
262-
}
263-
264-
if (this.isZero()) {
265-
throw new RangeError("Zero does not have a significand");
266-
}
267-
268-
if (this.isNegative()) {
269-
let [s, e] = this.negate().significandAndExponent();
270-
return [s.negate(), e];
271-
}
272-
273-
let v = this.cohort() as Rational;
274-
let q = this.quantum() as number;
275-
let s = v;
276-
let e = q;
277-
278-
while (s.cmp(ratOne) < 0) {
279-
s = s.scale10(1);
280-
e++;
281-
}
282-
283-
while (s.cmp(ratTen) >= 0) {
284-
s = s.scale10(-1);
285-
e--;
286-
}
287-
288-
return [s, e];
289-
}
290-
291250
public exponent(): number {
292251
let mantissa = this.mantissa();
293252
let mantissaQuantum = mantissa.quantum();
@@ -336,7 +295,12 @@ export class Decimal128 {
336295
return this.clone();
337296
}
338297

339-
let v = this.cohort() as Rational;
298+
let v = this.cohort();
299+
300+
if (v === "0" || v === "-0") {
301+
return this.clone();
302+
}
303+
340304
let q = this.quantum() as number;
341305

342306
return new Decimal128(
@@ -345,10 +309,6 @@ export class Decimal128 {
345309
}
346310

347311
private coefficient(): bigint {
348-
if (this.isZero()) {
349-
throw new RangeError("Zero does not have a significand");
350-
}
351-
352312
let d = this.d as Decimal;
353313
return d.coefficient();
354314
}
@@ -390,11 +350,6 @@ export class Decimal128 {
390350
}
391351

392352
let c = v.scale10(0 - q);
393-
394-
if (!c.isInteger()) {
395-
throw new TypeError("The coefficient is not an integer.");
396-
}
397-
398353
let s = c.numerator.toString();
399354
let p = this._isNegative ? "-" : "";
400355

@@ -446,11 +401,7 @@ export class Decimal128 {
446401

447402
if (!preserveTrailingZeroes && asDecimalString.match(/[.]/)) {
448403
asDecimalString = asDecimalString.replace(/0+$/, "");
449-
if (asDecimalString === "") {
450-
asDecimalString = "0";
451-
} else if (asDecimalString === "-") {
452-
asDecimalString = "-0";
453-
} else if (asDecimalString.match(/[.]$/)) {
404+
if (asDecimalString.match(/[.]$/)) {
454405
asDecimalString = asDecimalString.substring(
455406
0,
456407
asDecimalString.length - 1
@@ -505,22 +456,10 @@ export class Decimal128 {
505456

506457
if (roundedRendered.match(/[.]/)) {
507458
let [lhs, rhs] = roundedRendered.split(/[.]/);
508-
if (rhs.length < n) {
509-
return lhs + "." + rhs + "0".repeat(n - rhs.length);
510-
}
511-
512-
if (n === 0) {
513-
return lhs;
514-
}
515-
516459
return lhs + "." + rhs.substring(0, n);
517460
}
518461

519-
if (n === 0) {
520-
return roundedRendered;
521-
}
522-
523-
return roundedRendered + "." + "0".repeat(n);
462+
return roundedRendered;
524463
}
525464

526465
toPrecision(opts?: { digits?: number }): string {
@@ -560,30 +499,16 @@ export class Decimal128 {
560499
let p = this.isNegative() ? "-" : "";
561500

562501
if (n <= lhs.length) {
563-
if (lhs.match(/[.]$/)) {
564-
lhs = lhs.substring(0, n);
565-
}
566-
567502
if (lhs.length === n) {
568503
return p + lhs;
569504
}
570505

571-
if (1 === n) {
572-
return p + s.substring(0, 1) + "e+" + `${lhs.length - n}`;
573-
}
574-
575-
return (
576-
p +
577-
s.substring(0, 1) +
578-
"." +
579-
s.substring(1, n) +
580-
"e+" +
581-
`${lhs.length - n + 1}`
582-
);
506+
return p + s.substring(0, n) + "e+" + `${lhs.length - n + 1}`;
583507
}
584508

585509
if (n <= lhs.length + rhs.length) {
586-
return p + s.substring(0, n + 1); // plus one because of the decimal point
510+
let rounded = this.round(n - lhs.length);
511+
return rounded.emitDecimal();
587512
}
588513

589514
return p + lhs + "." + rhs + "0".repeat(n - lhs.length - rhs.length);
@@ -860,10 +785,6 @@ export class Decimal128 {
860785
let preferredQuantum = Math.min(ourExponent, theirExponent);
861786

862787
if (difference.isZero()) {
863-
if (this._isNegative) {
864-
difference = "-0";
865-
}
866-
867788
difference = "0";
868789
}
869790

@@ -1084,8 +1005,6 @@ export class Decimal128 {
10841005
}
10851006

10861007
let v = this.cohort() as Rational;
1087-
let q = this.quantum() as number;
1088-
10891008
let roundedV = v.round(numDecimalDigits, mode);
10901009

10911010
if (roundedV.isZero()) {

src/common.mts

-31
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,3 @@
1-
/**
2-
* Counts the number of significant digits in a digit string, assumed to be normalized.
3-
*
4-
* @param s
5-
*/
6-
export function countSignificantDigits(s: string): number {
7-
if (s.match(/^-/)) {
8-
return countSignificantDigits(s.substring(1));
9-
}
10-
11-
if (s.match(/^0[.]/)) {
12-
let m = s.match(/[.]0+/);
13-
14-
if (m) {
15-
return s.length - m[0].length - 1;
16-
}
17-
18-
return s.length - 2;
19-
}
20-
21-
if (s.match(/[.]/)) {
22-
return s.length - 1;
23-
}
24-
25-
let m = s.match(/^0+$/);
26-
27-
s = s.replace(/^0+$/, "");
28-
29-
return s.length;
30-
}
31-
321
export function countFractionalDigits(s: string): number {
332
let [, fractional] = s.split(".");
343

tests/Decimal128/cmp.test.js

+6
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,12 @@ describe("zero", () => {
145145
test("negative zero vs zero", () => {
146146
expect(negZero.cmp(zero)).toStrictEqual(0);
147147
});
148+
test("compare zero to positive", () => {
149+
expect(zero.cmp(one)).toStrictEqual(-1);
150+
});
151+
test("compare zero to negative", () => {
152+
expect(zero.cmp(one.negate())).toStrictEqual(1);
153+
});
148154
});
149155

150156
describe("normalization", () => {

tests/Decimal128/constructor.test.js

+20
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,26 @@ describe("constructor", () => {
123123
new Decimal128("-0." + "9".repeat(100)).toString()
124124
).toStrictEqual("-1");
125125
});
126+
test("zero with scale beyond limits (negative)", () => {
127+
expect(
128+
new Decimal128("0." + "0".repeat(10000)).toExponential()
129+
).toStrictEqual("0e-6176");
130+
});
131+
test("zero with scale beyond limits (positive)", () => {
132+
expect(new Decimal128("0E+10000").toExponential()).toStrictEqual(
133+
"0e+6111"
134+
);
135+
});
136+
test("minus zero with scale beyond limits (negative)", () => {
137+
expect(
138+
new Decimal128("-0." + "0".repeat(10000)).toExponential()
139+
).toStrictEqual("-0e-6176");
140+
});
141+
test("minus zero with scale beyond limits (positive)", () => {
142+
expect(new Decimal128("-0E+10000").toExponential()).toStrictEqual(
143+
"-0e+6111"
144+
);
145+
});
126146
test("lots of digits gets rounded to 10", () => {
127147
expect(
128148
new Decimal128("9." + "9".repeat(100)).toString()

tests/Decimal128/iszero.test.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Decimal128 } from "../../src/Decimal128.mjs";
2+
3+
describe("simple examples", () => {
4+
test("0", () => {
5+
expect(new Decimal128("0").isZero()).toStrictEqual(true);
6+
});
7+
test("0.00", () => {
8+
expect(new Decimal128("0.00").isZero()).toStrictEqual(true);
9+
});
10+
test("-0", () => {
11+
expect(new Decimal128("-0").isZero()).toStrictEqual(true);
12+
});
13+
test("-0.0", () => {
14+
expect(new Decimal128("-0.0").isZero()).toStrictEqual(true);
15+
});
16+
test("Infinity", () => {
17+
expect(new Decimal128("Infinity").isZero()).toStrictEqual(false);
18+
});
19+
test("-Infinity", () => {
20+
expect(new Decimal128("-Infinity").isZero()).toStrictEqual(false);
21+
});
22+
test("NaN", () => {
23+
expect(new Decimal128("NaN").isZero()).toStrictEqual(false);
24+
});
25+
test("42", () => {
26+
expect(new Decimal128("42").isZero()).toStrictEqual(false);
27+
});
28+
});

tests/Decimal128/multiply.test.js

+52-12
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import { Decimal128 } from "../../src/Decimal128.mjs";
22

3+
let posZero = new Decimal128("0");
4+
let negZero = new Decimal128("-0");
5+
36
const examples = [
4-
// ["123.456", "789.789", "97504.190784"],
5-
// ["2", "3", "6"],
6-
// ["2", "3.0", "6.0"],
7-
// ["2.0", "3.0", "6.00"],
8-
// ["4", "0.5", "2.0"],
9-
// ["10", "100", "1000"],
10-
// ["0.1", "0.2", "0.02"],
11-
// ["0.25", "1.5", "0.375"],
12-
// ["0.12345", "0.67890", "0.0838102050"],
13-
// ["0.123456789", "0.987654321", "0.121932631112635269"],
14-
// ["100000.123", "99999.321", "9999944399.916483"],
15-
// ["123456.123456789", "987654.987654321", "121932056088.565269013112635269"],
7+
["123.456", "789.789", "97504.190784"],
8+
["2", "3", "6"],
9+
["2", "3.0", "6.0"],
10+
["2.0", "3.0", "6.00"],
11+
["4", "0.5", "2.0"],
12+
["10", "100", "1000"],
13+
["0.1", "0.2", "0.02"],
14+
["0.25", "1.5", "0.375"],
15+
["0.12345", "0.67890", "0.0838102050"],
16+
["0.123456789", "0.987654321", "0.121932631112635269"],
17+
["100000.123", "99999.321", "9999944399.916483"],
18+
["123456.123456789", "987654.987654321", "121932056088.565269013112635269"],
1619
[
1720
"123456789.987654321",
1821
"987654321.123456789",
@@ -58,6 +61,43 @@ describe("multiplication", () => {
5861
)
5962
).toThrow(RangeError);
6063
});
64+
describe("zero", () => {
65+
let d = new Decimal128("42.65");
66+
test("left-hand positive zero", () => {
67+
expect(
68+
posZero.multiply(d).toString({ preserveTrailingZeroes: true })
69+
).toStrictEqual("0.00");
70+
});
71+
test("left-hand negative zero", () => {
72+
expect(
73+
negZero.multiply(d).toString({ preserveTrailingZeroes: true })
74+
).toStrictEqual("-0.00");
75+
});
76+
test("right-hand positive zero", () => {
77+
expect(
78+
d.multiply(posZero).toString({ preserveTrailingZeroes: true })
79+
).toStrictEqual("0.00");
80+
});
81+
test("right-hand negative zero", () => {
82+
expect(
83+
d.multiply(negZero).toString({ preserveTrailingZeroes: true })
84+
).toStrictEqual("-0.00");
85+
});
86+
test("quantum respected even with positive zero", () => {
87+
expect(
88+
new Decimal128("0.0")
89+
.multiply(posZero)
90+
.toString({ preserveTrailingZeroes: true })
91+
).toStrictEqual("0.0");
92+
});
93+
test("quantum respected even with negative zero", () => {
94+
expect(
95+
new Decimal128("-0.0")
96+
.multiply(posZero)
97+
.toString({ preserveTrailingZeroes: true })
98+
).toStrictEqual("-0.0");
99+
});
100+
});
61101
describe("NaN", () => {
62102
test("NaN times NaN is NaN", () => {
63103
expect(

tests/Decimal128/round.test.js

+5
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ describe("rounding", () => {
7171
new Decimal128("0.5").round(0, "trunc").toString()
7272
).toStrictEqual("0");
7373
});
74+
test("round to minus zero", () => {
75+
expect(
76+
new Decimal128("-0.5").round(0, "trunc").toString()
77+
).toStrictEqual("-0");
78+
});
7479
});
7580

7681
describe("unsupported rounding mode", () => {

0 commit comments

Comments
 (0)