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

Commit 542d00e

Browse files
committed
WIP
1 parent 149b3c5 commit 542d00e

File tree

5 files changed

+401
-262
lines changed

5 files changed

+401
-262
lines changed

src/Decimal.mts

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { Rational } from "./rational.mjs";
2+
3+
function _cohort(s: string): "0" | "-0" | Rational {
4+
if (s === "0" || s === "-0") {
5+
return s;
6+
}
7+
8+
return Rational.fromString(s);
9+
}
10+
11+
function _quantum(s: string): number {
12+
if (s.match(/^-/)) {
13+
return _quantum(s.substring(1));
14+
}
15+
16+
if (!s.match(/[.]/)) {
17+
return 0;
18+
}
19+
20+
let [_, rhs] = s.split(".");
21+
22+
if (rhs.match(/[eE]/)) {
23+
let [dec, exp] = rhs.split(/[eE]/);
24+
return parseInt(exp) - dec.length;
25+
}
26+
27+
return 0 - rhs.length;
28+
}
29+
30+
interface CohortAndQuantum {
31+
cohort: "0" | "-0" | Rational;
32+
quantum: number;
33+
}
34+
35+
export class Decimal {
36+
public readonly cohort: "0" | "-0" | Rational;
37+
public readonly quantum: number;
38+
39+
constructor(x: string | CohortAndQuantum) {
40+
let v = typeof x === "string" ? _cohort(x) : x.cohort;
41+
let q = typeof x === "string" ? _quantum(x) : x.quantum;
42+
43+
if (v instanceof Rational && v.isZero()) {
44+
throw new RangeError("A rational number cohort must not be zero.");
45+
}
46+
47+
if (!Number.isInteger(q)) {
48+
throw new Error("The quantum must be an integer.");
49+
}
50+
51+
if (v instanceof Rational) {
52+
let scaledV = v.scale10(0 - q);
53+
54+
if (!scaledV.isInteger()) {
55+
throw new RangeError(
56+
`Scaled value is not an integer (v = ${v}, q = ${q})`
57+
);
58+
}
59+
}
60+
61+
this.cohort = v;
62+
this.quantum = q;
63+
}
64+
65+
public isZero(): boolean {
66+
let v = this.cohort;
67+
return v === "0" || v === "-0";
68+
}
69+
70+
public isInteger(): boolean {
71+
let v = this.cohort;
72+
73+
if (v === "0" || v === "-0") {
74+
return true;
75+
}
76+
77+
return v.isInteger();
78+
}
79+
}

src/common.mts

+60-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
import {
2+
ROUNDING_MODE_CEILING,
3+
ROUNDING_MODE_FLOOR,
4+
ROUNDING_MODE_HALF_EXPAND,
5+
ROUNDING_MODE_TRUNCATE,
6+
} from "./decimal128.mjs";
7+
18
/**
29
* Counts the number of significant digits in a digit string, assumed to be normalized.
310
*
@@ -33,7 +40,7 @@ export function countDigits(s: string): number {
3340
return s.replace(/[.]/, "").length;
3441
}
3542

36-
export function countFractionaDigits(s: string): number {
43+
export function countFractionalDigits(s: string): number {
3744
let [, fractional] = s.split(".");
3845

3946
if (undefined === fractional) {
@@ -60,3 +67,55 @@ export const ROUNDING_MODES: RoundingMode[] = [
6067
"halfEven",
6168
"halfExpand",
6269
];
70+
71+
function roundIt(
72+
isNegative: boolean,
73+
digitToRound: Digit,
74+
decidingDigit: Digit,
75+
roundingMode: RoundingMode
76+
): DigitOrTen {
77+
switch (roundingMode) {
78+
case ROUNDING_MODE_CEILING:
79+
if (isNegative) {
80+
return digitToRound;
81+
}
82+
83+
if (0 === decidingDigit) {
84+
return digitToRound;
85+
}
86+
87+
return (digitToRound + 1) as DigitOrTen;
88+
case ROUNDING_MODE_FLOOR:
89+
if (0 === decidingDigit) {
90+
return digitToRound;
91+
}
92+
93+
if (isNegative) {
94+
return (digitToRound + 1) as DigitOrTen;
95+
}
96+
97+
return digitToRound;
98+
case ROUNDING_MODE_TRUNCATE:
99+
return digitToRound;
100+
case ROUNDING_MODE_HALF_EXPAND:
101+
if (decidingDigit >= 5) {
102+
return (digitToRound + 1) as DigitOrTen;
103+
}
104+
105+
return digitToRound;
106+
default: // ROUNDING_MODE_HALF_EVEN:
107+
if (decidingDigit === 5) {
108+
if (digitToRound % 2 === 0) {
109+
return digitToRound;
110+
}
111+
112+
return (digitToRound + 1) as DigitOrTen;
113+
}
114+
115+
if (decidingDigit > 5) {
116+
return (digitToRound + 1) as DigitOrTen;
117+
}
118+
119+
return digitToRound;
120+
}
121+
}

0 commit comments

Comments
 (0)