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

Commit 928aec2

Browse files
committed
WIP
1 parent 542d00e commit 928aec2

File tree

5 files changed

+231
-104
lines changed

5 files changed

+231
-104
lines changed

src/Decimal.mts

+145-10
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,29 @@
11
import { Rational } from "./rational.mjs";
22

3+
const ratOne = new Rational(1n, 1n);
4+
const ratTen = new Rational(10n, 1n);
5+
36
function _cohort(s: string): "0" | "-0" | Rational {
4-
if (s === "0" || s === "-0") {
5-
return s;
7+
if (s.match(/^-/)) {
8+
let c = _cohort(s.substring(1));
9+
10+
if (c === "0") {
11+
return "-0";
12+
}
13+
14+
if (c === "-0") {
15+
return "0";
16+
}
17+
18+
return c.negate();
19+
}
20+
21+
if (s.match(/^00+/)) {
22+
return _cohort(s.substring(1));
23+
}
24+
25+
if (s.match(/^0([.]0+)?$/)) {
26+
return "0";
627
}
728

829
return Rational.fromString(s);
@@ -13,18 +34,23 @@ function _quantum(s: string): number {
1334
return _quantum(s.substring(1));
1435
}
1536

16-
if (!s.match(/[.]/)) {
17-
return 0;
18-
}
37+
if (s.match(/[.]/)) {
38+
let [_, rhs] = s.split(".");
1939

20-
let [_, rhs] = s.split(".");
40+
if (rhs.match(/[eE]/)) {
41+
let [dec, exp] = rhs.split(/[eE]/);
42+
return parseInt(exp) - dec.length;
43+
}
44+
45+
return 0 - rhs.length;
46+
}
2147

22-
if (rhs.match(/[eE]/)) {
23-
let [dec, exp] = rhs.split(/[eE]/);
24-
return parseInt(exp) - dec.length;
48+
if (s.match(/[eE]/)) {
49+
let [dec, exp] = s.split(/[eE]/);
50+
return parseInt(exp);
2551
}
2652

27-
return 0 - rhs.length;
53+
return 0;
2854
}
2955

3056
interface CohortAndQuantum {
@@ -76,4 +102,113 @@ export class Decimal {
76102

77103
return v.isInteger();
78104
}
105+
106+
public isNegative(): boolean {
107+
let v = this.cohort;
108+
109+
if (v === "0") {
110+
return false;
111+
}
112+
113+
if (v === "-0") {
114+
return true;
115+
}
116+
117+
return v.isNegative;
118+
}
119+
120+
public scale10(n: number, adjustQuantum?: boolean): Decimal {
121+
if (!Number.isInteger(n)) {
122+
throw new Error("The scale factor must be an integer.");
123+
}
124+
125+
if (0 === n) {
126+
return this;
127+
}
128+
129+
let v = this.cohort;
130+
let newQuantum = this.quantum;
131+
132+
if (typeof adjustQuantum === "boolean" && adjustQuantum) {
133+
if (n < 0) {
134+
newQuantum -= n;
135+
} else {
136+
newQuantum += n;
137+
}
138+
}
139+
140+
if (v === "0" || v === "-0") {
141+
return new Decimal({ cohort: v, quantum: newQuantum });
142+
}
143+
144+
return new Decimal({
145+
cohort: v.scale10(n),
146+
quantum: newQuantum,
147+
});
148+
}
149+
150+
public negate(): Decimal {
151+
let v = this.cohort;
152+
153+
if (v === "0") {
154+
return new Decimal({ cohort: "-0", quantum: this.quantum });
155+
}
156+
157+
if (v === "-0") {
158+
return new Decimal({ cohort: "0", quantum: this.quantum });
159+
}
160+
161+
return new Decimal({
162+
cohort: v.negate(),
163+
quantum: this.quantum,
164+
});
165+
}
166+
167+
public significand(): Rational {
168+
if (this.isNegative()) {
169+
return this.negate().significand();
170+
}
171+
172+
let v = this.cohort;
173+
174+
if (v === "0" || v === "-0") {
175+
throw new RangeError("Cannot compute coefficient of zero.");
176+
}
177+
178+
while (ratTen.lessThan(v) || ratTen.equals(v)) {
179+
v = v.scale10(-1);
180+
}
181+
182+
while (v.lessThan(ratOne)) {
183+
v = v.scale10(1);
184+
}
185+
186+
return v;
187+
}
188+
189+
public exponent(): number {
190+
if (this.isNegative()) {
191+
return this.negate().exponent();
192+
}
193+
194+
let v = this.cohort;
195+
196+
if (v === "0" || v === "-0") {
197+
throw new RangeError("Cannot compute coefficient of zero.");
198+
}
199+
200+
let e = 0;
201+
202+
while (ratTen.lessThan(v) || ratTen.equals(v)) {
203+
v = v.scale10(-1);
204+
e++;
205+
}
206+
207+
while (v.lessThan(ratOne)) {
208+
v = v.scale10(1);
209+
e--;
210+
}
211+
212+
return e;
213+
}
79214
}

src/decimal128.mts

+21-12
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,19 @@ function pickQuantum(d: Rational, preferredQuantum: number): number {
4343
}
4444

4545
function adjustDecimal128(v: Rational, q: number): Decimal {
46+
if (v.isNegative) {
47+
return adjustDecimal128(v.negate(), q).negate();
48+
}
49+
50+
let x = new Decimal({ cohort: v, quantum: q });
51+
4652
if (
4753
v
4854
.abs()
4955
.scale10(0 - q)
5056
.cmp(TEN_MAX_EXPONENT) <= 0
5157
) {
52-
return new Decimal({ cohort: v, quantum: q });
58+
return x;
5359
}
5460

5561
if (v.isInteger()) {
@@ -61,19 +67,14 @@ function adjustDecimal128(v: Rational, q: number): Decimal {
6167
throw new RangeError("Integer too large");
6268
}
6369

64-
let s = v.toPrecision(MAX_SIGNIFICANT_DIGITS + 1);
65-
let numFractionalDigits = 0;
66-
67-
if (s.match(/[.]/)) {
68-
let [_, rhs] = s.split(".");
69-
numFractionalDigits = rhs.length;
70-
}
71-
72-
let rounded = v.round(numFractionalDigits - 1, "halfEven");
70+
let sig = x.significand();
71+
let exp = x.exponent();
7372

73+
let scaledSig = sig.scale10(MAX_SIGNIFICANT_DIGITS - 1);
74+
let rounded = scaledSig.round(0, "halfEven");
7475
return new Decimal({
75-
cohort: rounded,
76-
quantum: 0 - numFractionalDigits,
76+
cohort: rounded.scale10(0 - MAX_SIGNIFICANT_DIGITS + exp + 1),
77+
quantum: 0 - MAX_SIGNIFICANT_DIGITS + exp,
7778
});
7879
}
7980

@@ -188,6 +189,14 @@ export class Decimal128 {
188189
this._isFinite = false;
189190
this._isNegative = true;
190191
} else {
192+
let v = data.cohort;
193+
if (v === "-0") {
194+
this._isNegative = true;
195+
} else if (v === "0") {
196+
this._isNegative = false;
197+
} else {
198+
this._isNegative = v.isNegative;
199+
}
191200
this.d = data;
192201
}
193202
}

src/rational.mts

+16-28
Original file line numberDiff line numberDiff line change
@@ -171,16 +171,8 @@ export class Rational {
171171

172172
if (s.match(/^[0-9]+[eE][+-]?[0-9]+$/)) {
173173
let [num, exp] = s.split(/[eE]/);
174-
if (exp.match(/-/)) {
175-
let [_, fractional] = exp.split("-");
176-
let originalRat = new Rational(BigInt(num), 1n);
177-
return Rational.divide(
178-
originalRat,
179-
new Rational(1n, BigInt(10) ** BigInt(fractional))
180-
);
181-
}
182-
183-
return new Rational(BigInt(num), ten ** BigInt(exp));
174+
let originalRat = new Rational(BigInt(num), 1n);
175+
return originalRat.scale10(Number(exp));
184176
}
185177

186178
if (s.match(/[.]/)) {
@@ -189,26 +181,10 @@ export class Rational {
189181
if (decimal.match(/[eE]/)) {
190182
let [dec, exp] = decimal.split(/[eE]/);
191183
let originalRat = Rational.fromString(`${whole}.${dec}`);
192-
if (exp.match(/-/)) {
193-
let [_, fractional] = exp.split("-");
194-
return Rational.divide(
195-
originalRat,
196-
new Rational(1n, ten ** BigInt(fractional))
197-
);
198-
}
199-
200-
return Rational.multiply(
201-
originalRat,
202-
new Rational(1n, ten ** BigInt(exp))
203-
);
184+
return originalRat.scale10(Number(exp));
204185
}
205186

206-
let wholePart = BigInt(whole);
207-
let decimalPart = BigInt(decimal);
208-
let numDigits = BigInt(
209-
wholePart.toString().length + decimal.length
210-
);
211-
let numerator = wholePart * ten ** numDigits + decimalPart;
187+
let numerator = BigInt(whole + decimal);
212188
let denominator = ten ** BigInt(decimal.length);
213189
return new Rational(numerator, denominator);
214190
}
@@ -223,6 +199,10 @@ export class Rational {
223199
);
224200
}
225201

202+
if (this.isNegative) {
203+
return this.negate().scale10(n).negate();
204+
}
205+
226206
if (n === 0) {
227207
return this;
228208
}
@@ -500,6 +480,14 @@ export class Rational {
500480
return 0;
501481
}
502482

483+
lessThan(x: Rational): boolean {
484+
return this.cmp(x) === -1;
485+
}
486+
487+
equals(x: Rational): boolean {
488+
return this.cmp(x) === 0;
489+
}
490+
503491
isZero(): boolean {
504492
return this.numerator === zero;
505493
}

0 commit comments

Comments
 (0)