@obinexusmk2/hypernum 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +355 -256
- package/dist/index.cjs +4 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -7
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +2 -2
- package/dist/index.umd.js.map +1 -1
- package/dist/types/{config → src/config}/config-loader.d.ts +0 -0
- package/dist/types/src/config/config-loader.d.ts.map +1 -0
- package/dist/types/{config → src/config}/config-parser.d.ts +0 -0
- package/dist/types/src/config/config-parser.d.ts.map +1 -0
- package/dist/types/{config → src/config}/config-resolver.d.ts +0 -0
- package/dist/types/src/config/config-resolver.d.ts.map +1 -0
- package/dist/types/{config → src/config}/config-source.d.ts +0 -0
- package/dist/types/src/config/config-source.d.ts.map +1 -0
- package/dist/types/{config → src/config}/index.d.ts +0 -0
- package/dist/types/src/config/index.d.ts.map +1 -0
- package/dist/types/{core → src/core}/common.d.ts +0 -0
- package/dist/types/src/core/common.d.ts.map +1 -0
- package/dist/types/{core → src/core}/config.d.ts +0 -0
- package/dist/types/src/core/config.d.ts.map +1 -0
- package/dist/types/{core → src/core}/constants.d.ts +0 -0
- package/dist/types/src/core/constants.d.ts.map +1 -0
- package/dist/types/{core → src/core}/errors.d.ts +0 -0
- package/dist/types/src/core/errors.d.ts.map +1 -0
- package/dist/types/{core → src/core}/hypernum.d.ts +0 -0
- package/dist/types/src/core/hypernum.d.ts.map +1 -0
- package/dist/types/{core → src/core}/index.d.ts +0 -0
- package/dist/types/src/core/index.d.ts.map +1 -0
- package/dist/types/{index.d.ts → src/index.d.ts} +1 -1
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/{operations → src/operations}/arithmetic.d.ts +0 -0
- package/dist/types/src/operations/arithmetic.d.ts.map +1 -0
- package/dist/types/{operations → src/operations}/bitwise.d.ts +0 -0
- package/dist/types/src/operations/bitwise.d.ts.map +1 -0
- package/dist/types/{operations → src/operations}/comparison.d.ts +0 -0
- package/dist/types/src/operations/comparison.d.ts.map +1 -0
- package/dist/types/{operations → src/operations}/conversion.d.ts +0 -0
- package/dist/types/src/operations/conversion.d.ts.map +1 -0
- package/dist/types/{operations → src/operations}/factorial.d.ts +0 -0
- package/dist/types/src/operations/factorial.d.ts.map +1 -0
- package/dist/types/{operations → src/operations}/index.d.ts +0 -0
- package/dist/types/src/operations/index.d.ts.map +1 -0
- package/dist/types/{operations → src/operations}/power.d.ts +0 -0
- package/dist/types/src/operations/power.d.ts.map +1 -0
- package/dist/types/{storage → src/storage}/Heap.d.ts +0 -0
- package/dist/types/src/storage/Heap.d.ts.map +1 -0
- package/dist/types/{storage → src/storage}/index.d.ts +0 -0
- package/dist/types/src/storage/index.d.ts.map +1 -0
- package/dist/types/{structures → src/structures}/ackermann.d.ts +0 -0
- package/dist/types/src/structures/ackermann.d.ts.map +1 -0
- package/dist/types/{structures → src/structures}/big-array.d.ts +0 -0
- package/dist/types/src/structures/big-array.d.ts.map +1 -0
- package/dist/types/{structures → src/structures}/index.d.ts +0 -0
- package/dist/types/src/structures/index.d.ts.map +1 -0
- package/dist/types/{structures → src/structures}/number-tree.d.ts +0 -0
- package/dist/types/src/structures/number-tree.d.ts.map +1 -0
- package/dist/types/{structures → src/structures}/power-tower.d.ts +0 -0
- package/dist/types/src/structures/power-tower.d.ts.map +1 -0
- package/dist/types/{utils → src/utils}/formatting.d.ts +0 -0
- package/dist/types/src/utils/formatting.d.ts.map +1 -0
- package/dist/types/{utils → src/utils}/index.d.ts +0 -0
- package/dist/types/src/utils/index.d.ts.map +1 -0
- package/dist/types/{utils → src/utils}/parser.d.ts +0 -0
- package/dist/types/src/utils/parser.d.ts.map +1 -0
- package/dist/types/{utils → src/utils}/precision.d.ts +0 -0
- package/dist/types/src/utils/precision.d.ts.map +1 -0
- package/dist/types/{utils → src/utils}/validation.d.ts +0 -0
- package/dist/types/src/utils/validation.d.ts.map +1 -0
- package/package.json +169 -164
- package/rollup.config.js +163 -161
- package/src/cli/hypernum.js +272 -0
- package/src/config/config-loader.ts +0 -0
- package/src/config/config-parser.ts +160 -160
- package/src/config/config-resolver.ts +0 -0
- package/src/config/config-source.ts +0 -0
- package/src/config/index.ts +0 -0
- package/src/core/common.ts +184 -184
- package/src/core/config.ts +392 -392
- package/src/core/constants.ts +101 -101
- package/src/core/errors.ts +202 -202
- package/src/core/hypernum.ts +240 -240
- package/src/core/index.ts +4 -4
- package/src/index.ts +179 -182
- package/src/operations/arithmetic.ts +332 -332
- package/src/operations/bitwise.ts +366 -366
- package/src/operations/comparison.ts +271 -271
- package/src/operations/conversion.ts +399 -399
- package/src/operations/factorial.ts +278 -278
- package/src/operations/index.ts +4 -4
- package/src/operations/power.ts +315 -315
- package/src/storage/Heap.ts +237 -237
- package/src/storage/index.ts +0 -0
- package/src/structures/ackermann.ts +232 -232
- package/src/structures/big-array.ts +305 -305
- package/src/structures/index.ts +3 -3
- package/src/structures/number-tree.ts +403 -403
- package/src/structures/power-tower.ts +277 -277
- package/src/types/common.d.ts +356 -356
- package/src/types/core.d.ts +160 -160
- package/src/types/index.d.ts +1 -1
- package/src/utils/formatting.ts +245 -245
- package/src/utils/index.ts +4 -4
- package/src/utils/parser.ts +244 -244
- package/src/utils/precision.ts +216 -216
- package/src/utils/validation.ts +182 -182
- package/tsconfig.json +83 -83
- package/dist/types/config/config-loader.d.ts.map +0 -1
- package/dist/types/config/config-parser.d.ts.map +0 -1
- package/dist/types/config/config-resolver.d.ts.map +0 -1
- package/dist/types/config/config-source.d.ts.map +0 -1
- package/dist/types/config/index.d.ts.map +0 -1
- package/dist/types/core/common.d.ts.map +0 -1
- package/dist/types/core/config.d.ts.map +0 -1
- package/dist/types/core/constants.d.ts.map +0 -1
- package/dist/types/core/errors.d.ts.map +0 -1
- package/dist/types/core/hypernum.d.ts.map +0 -1
- package/dist/types/core/index.d.ts.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/operations/arithmetic.d.ts.map +0 -1
- package/dist/types/operations/bitwise.d.ts.map +0 -1
- package/dist/types/operations/comparison.d.ts.map +0 -1
- package/dist/types/operations/conversion.d.ts.map +0 -1
- package/dist/types/operations/factorial.d.ts.map +0 -1
- package/dist/types/operations/index.d.ts.map +0 -1
- package/dist/types/operations/power.d.ts.map +0 -1
- package/dist/types/storage/Heap.d.ts.map +0 -1
- package/dist/types/storage/index.d.ts.map +0 -1
- package/dist/types/structures/ackermann.d.ts.map +0 -1
- package/dist/types/structures/big-array.d.ts.map +0 -1
- package/dist/types/structures/index.d.ts.map +0 -1
- package/dist/types/structures/number-tree.d.ts.map +0 -1
- package/dist/types/structures/power-tower.d.ts.map +0 -1
- package/dist/types/utils/formatting.d.ts.map +0 -1
- package/dist/types/utils/index.d.ts.map +0 -1
- package/dist/types/utils/parser.d.ts.map +0 -1
- package/dist/types/utils/precision.d.ts.map +0 -1
- package/dist/types/utils/validation.d.ts.map +0 -1
package/src/utils/precision.ts
CHANGED
|
@@ -1,217 +1,217 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Precision utilities for Hypernum library
|
|
3
|
-
* Provides functions for handling decimal precision and rounding operations
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { ValidationError } from './validation';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Rounding modes for decimal operations
|
|
10
|
-
*/
|
|
11
|
-
export enum RoundingMode {
|
|
12
|
-
FLOOR = 'FLOOR', // Round towards negative infinity
|
|
13
|
-
CEIL = 'CEIL', // Round towards positive infinity
|
|
14
|
-
DOWN = 'DOWN', // Round towards zero
|
|
15
|
-
UP = 'UP', // Round away from zero
|
|
16
|
-
HALF_EVEN = 'HALF_EVEN', // Round to nearest even number when tied (Banker's rounding)
|
|
17
|
-
HALF_UP = 'HALF_UP', // Round up when tied
|
|
18
|
-
HALF_DOWN = 'HALF_DOWN', // Round down when tied
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Scale a bigint by a power of 10
|
|
23
|
-
*/
|
|
24
|
-
export const scaleByPowerOfTen = (value: bigint, power: number): bigint => {
|
|
25
|
-
if (power === 0) return value;
|
|
26
|
-
if (power > 0) {
|
|
27
|
-
return value * (BigInt(10) ** BigInt(power));
|
|
28
|
-
}
|
|
29
|
-
return value / (BigInt(10) ** BigInt(-power));
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Round a number according to specified mode and precision
|
|
34
|
-
*/
|
|
35
|
-
export const round = (
|
|
36
|
-
value: bigint,
|
|
37
|
-
precision: number = 0,
|
|
38
|
-
mode: RoundingMode = RoundingMode.HALF_EVEN
|
|
39
|
-
): bigint => {
|
|
40
|
-
if (precision < 0) {
|
|
41
|
-
throw new ValidationError('Precision must be non-negative');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (precision === 0) {
|
|
45
|
-
return value;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const scale = BigInt(10) ** BigInt(precision);
|
|
49
|
-
const scaled = value / scale;
|
|
50
|
-
const remainder = value % scale;
|
|
51
|
-
|
|
52
|
-
switch (mode) {
|
|
53
|
-
case RoundingMode.FLOOR:
|
|
54
|
-
return scaled * scale;
|
|
55
|
-
|
|
56
|
-
case RoundingMode.CEIL:
|
|
57
|
-
return remainder > 0n ? (scaled + 1n) * scale : scaled * scale;
|
|
58
|
-
|
|
59
|
-
case RoundingMode.DOWN:
|
|
60
|
-
return value >= 0n ? scaled * scale : (scaled - 1n) * scale;
|
|
61
|
-
|
|
62
|
-
case RoundingMode.UP:
|
|
63
|
-
return value >= 0n ? (scaled + 1n) * scale : scaled * scale;
|
|
64
|
-
|
|
65
|
-
case RoundingMode.HALF_UP:
|
|
66
|
-
return remainder >= scale / 2n ? (scaled + 1n) * scale : scaled * scale;
|
|
67
|
-
|
|
68
|
-
case RoundingMode.HALF_DOWN:
|
|
69
|
-
return remainder > scale / 2n ? (scaled + 1n) * scale : scaled * scale;
|
|
70
|
-
|
|
71
|
-
case RoundingMode.HALF_EVEN:
|
|
72
|
-
if (remainder === scale / 2n) {
|
|
73
|
-
return scaled % 2n === 0n ? scaled * scale : (scaled + 1n) * scale;
|
|
74
|
-
}
|
|
75
|
-
return remainder > scale / 2n ? (scaled + 1n) * scale : scaled * scale;
|
|
76
|
-
|
|
77
|
-
default:
|
|
78
|
-
throw new ValidationError('Invalid rounding mode');
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Calculate precision required to represent a number without loss
|
|
84
|
-
*/
|
|
85
|
-
export const calculateRequiredPrecision = (value: bigint): number => {
|
|
86
|
-
if (value === 0n) return 0;
|
|
87
|
-
|
|
88
|
-
const str = value.toString();
|
|
89
|
-
const nonZeroIndex = str.split('').reverse().findIndex(char => char !== '0');
|
|
90
|
-
return nonZeroIndex === -1 ? 0 : nonZeroIndex;
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Normalize two numbers to the same precision
|
|
95
|
-
*/
|
|
96
|
-
export const normalizePrecision = (
|
|
97
|
-
a: bigint,
|
|
98
|
-
b: bigint,
|
|
99
|
-
precisionA: number,
|
|
100
|
-
precisionB: number
|
|
101
|
-
): [bigint, bigint] => {
|
|
102
|
-
const targetPrecision = Math.max(precisionA, precisionB);
|
|
103
|
-
|
|
104
|
-
const scaledA = scaleByPowerOfTen(a, targetPrecision - precisionA);
|
|
105
|
-
const scaledB = scaleByPowerOfTen(b, targetPrecision - precisionB);
|
|
106
|
-
|
|
107
|
-
return [scaledA, scaledB];
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Scale a division operation to achieve desired precision
|
|
112
|
-
*/
|
|
113
|
-
export const scaledDivision = (
|
|
114
|
-
numerator: bigint,
|
|
115
|
-
denominator: bigint,
|
|
116
|
-
precision: number,
|
|
117
|
-
roundingMode: RoundingMode = RoundingMode.HALF_EVEN
|
|
118
|
-
): bigint => {
|
|
119
|
-
if (denominator === 0n) {
|
|
120
|
-
throw new ValidationError('Division by zero');
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (precision < 0) {
|
|
124
|
-
throw new ValidationError('Precision must be non-negative');
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Scale up numerator to handle desired precision
|
|
128
|
-
const scaledNumerator = scaleByPowerOfTen(numerator, precision);
|
|
129
|
-
const quotient = scaledNumerator / denominator;
|
|
130
|
-
|
|
131
|
-
return round(quotient, 0, roundingMode);
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Calculate the number of significant digits
|
|
136
|
-
*/
|
|
137
|
-
export const significantDigits = (value: bigint): number => {
|
|
138
|
-
const nonZeroPattern = /[1-9]/;
|
|
139
|
-
const str = value.toString();
|
|
140
|
-
const firstSignificant = str.search(nonZeroPattern);
|
|
141
|
-
if (firstSignificant === -1) return 0;
|
|
142
|
-
|
|
143
|
-
const lastSignificant = str.split('').reverse().findIndex(char => char !== '0');
|
|
144
|
-
return str.length - firstSignificant - (lastSignificant === -1 ? 0 : lastSignificant);
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Truncate to specified number of significant digits
|
|
149
|
-
*/
|
|
150
|
-
export const truncateToSignificantDigits = (
|
|
151
|
-
value: bigint,
|
|
152
|
-
digits: number,
|
|
153
|
-
roundingMode: RoundingMode = RoundingMode.HALF_EVEN
|
|
154
|
-
): bigint => {
|
|
155
|
-
if (digits <= 0) {
|
|
156
|
-
throw new ValidationError('Number of significant digits must be positive');
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const currentDigits = significantDigits(value);
|
|
160
|
-
|
|
161
|
-
if (currentDigits <= digits) {
|
|
162
|
-
return value;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const scale = currentDigits - digits;
|
|
166
|
-
return round(value, scale, roundingMode);
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Check if two numbers are equal within a specified precision
|
|
171
|
-
*/
|
|
172
|
-
export const equalWithinPrecision = (
|
|
173
|
-
a: bigint,
|
|
174
|
-
b: bigint,
|
|
175
|
-
precision: number
|
|
176
|
-
): boolean => {
|
|
177
|
-
const diff = a - b;
|
|
178
|
-
const tolerance = BigInt(10) ** BigInt(precision);
|
|
179
|
-
return diff.toString().length <= tolerance.toString().length;
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Get the fractional part of a number at a given precision
|
|
184
|
-
*/
|
|
185
|
-
export const getFractionalPart = (
|
|
186
|
-
value: bigint,
|
|
187
|
-
precision: number
|
|
188
|
-
): bigint => {
|
|
189
|
-
if (precision <= 0) return 0n;
|
|
190
|
-
|
|
191
|
-
const scale = BigInt(10) ** BigInt(precision);
|
|
192
|
-
return value % scale;
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Format a number with exact precision (no rounding)
|
|
197
|
-
*/
|
|
198
|
-
export const toExactPrecision = (value: bigint, precision: number): string => {
|
|
199
|
-
if (precision < 0) {
|
|
200
|
-
throw new ValidationError('Precision must be non-negative');
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
let str = value.toString();
|
|
204
|
-
const isNegative = str.startsWith('-');
|
|
205
|
-
if (isNegative) {
|
|
206
|
-
str = str.slice(1);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
while (str.length <= precision) {
|
|
210
|
-
str = '0' + str;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const integerPart = str.slice(0, -precision) || '0';
|
|
214
|
-
const fractionalPart = str.slice(-precision);
|
|
215
|
-
|
|
216
|
-
return `${isNegative ? '-' : ''}${integerPart}.${fractionalPart}`;
|
|
1
|
+
/**
|
|
2
|
+
* Precision utilities for Hypernum library
|
|
3
|
+
* Provides functions for handling decimal precision and rounding operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ValidationError } from './validation';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Rounding modes for decimal operations
|
|
10
|
+
*/
|
|
11
|
+
export enum RoundingMode {
|
|
12
|
+
FLOOR = 'FLOOR', // Round towards negative infinity
|
|
13
|
+
CEIL = 'CEIL', // Round towards positive infinity
|
|
14
|
+
DOWN = 'DOWN', // Round towards zero
|
|
15
|
+
UP = 'UP', // Round away from zero
|
|
16
|
+
HALF_EVEN = 'HALF_EVEN', // Round to nearest even number when tied (Banker's rounding)
|
|
17
|
+
HALF_UP = 'HALF_UP', // Round up when tied
|
|
18
|
+
HALF_DOWN = 'HALF_DOWN', // Round down when tied
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Scale a bigint by a power of 10
|
|
23
|
+
*/
|
|
24
|
+
export const scaleByPowerOfTen = (value: bigint, power: number): bigint => {
|
|
25
|
+
if (power === 0) return value;
|
|
26
|
+
if (power > 0) {
|
|
27
|
+
return value * (BigInt(10) ** BigInt(power));
|
|
28
|
+
}
|
|
29
|
+
return value / (BigInt(10) ** BigInt(-power));
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Round a number according to specified mode and precision
|
|
34
|
+
*/
|
|
35
|
+
export const round = (
|
|
36
|
+
value: bigint,
|
|
37
|
+
precision: number = 0,
|
|
38
|
+
mode: RoundingMode = RoundingMode.HALF_EVEN
|
|
39
|
+
): bigint => {
|
|
40
|
+
if (precision < 0) {
|
|
41
|
+
throw new ValidationError('Precision must be non-negative');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (precision === 0) {
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const scale = BigInt(10) ** BigInt(precision);
|
|
49
|
+
const scaled = value / scale;
|
|
50
|
+
const remainder = value % scale;
|
|
51
|
+
|
|
52
|
+
switch (mode) {
|
|
53
|
+
case RoundingMode.FLOOR:
|
|
54
|
+
return scaled * scale;
|
|
55
|
+
|
|
56
|
+
case RoundingMode.CEIL:
|
|
57
|
+
return remainder > 0n ? (scaled + 1n) * scale : scaled * scale;
|
|
58
|
+
|
|
59
|
+
case RoundingMode.DOWN:
|
|
60
|
+
return value >= 0n ? scaled * scale : (scaled - 1n) * scale;
|
|
61
|
+
|
|
62
|
+
case RoundingMode.UP:
|
|
63
|
+
return value >= 0n ? (scaled + 1n) * scale : scaled * scale;
|
|
64
|
+
|
|
65
|
+
case RoundingMode.HALF_UP:
|
|
66
|
+
return remainder >= scale / 2n ? (scaled + 1n) * scale : scaled * scale;
|
|
67
|
+
|
|
68
|
+
case RoundingMode.HALF_DOWN:
|
|
69
|
+
return remainder > scale / 2n ? (scaled + 1n) * scale : scaled * scale;
|
|
70
|
+
|
|
71
|
+
case RoundingMode.HALF_EVEN:
|
|
72
|
+
if (remainder === scale / 2n) {
|
|
73
|
+
return scaled % 2n === 0n ? scaled * scale : (scaled + 1n) * scale;
|
|
74
|
+
}
|
|
75
|
+
return remainder > scale / 2n ? (scaled + 1n) * scale : scaled * scale;
|
|
76
|
+
|
|
77
|
+
default:
|
|
78
|
+
throw new ValidationError('Invalid rounding mode');
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Calculate precision required to represent a number without loss
|
|
84
|
+
*/
|
|
85
|
+
export const calculateRequiredPrecision = (value: bigint): number => {
|
|
86
|
+
if (value === 0n) return 0;
|
|
87
|
+
|
|
88
|
+
const str = value.toString();
|
|
89
|
+
const nonZeroIndex = str.split('').reverse().findIndex(char => char !== '0');
|
|
90
|
+
return nonZeroIndex === -1 ? 0 : nonZeroIndex;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Normalize two numbers to the same precision
|
|
95
|
+
*/
|
|
96
|
+
export const normalizePrecision = (
|
|
97
|
+
a: bigint,
|
|
98
|
+
b: bigint,
|
|
99
|
+
precisionA: number,
|
|
100
|
+
precisionB: number
|
|
101
|
+
): [bigint, bigint] => {
|
|
102
|
+
const targetPrecision = Math.max(precisionA, precisionB);
|
|
103
|
+
|
|
104
|
+
const scaledA = scaleByPowerOfTen(a, targetPrecision - precisionA);
|
|
105
|
+
const scaledB = scaleByPowerOfTen(b, targetPrecision - precisionB);
|
|
106
|
+
|
|
107
|
+
return [scaledA, scaledB];
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Scale a division operation to achieve desired precision
|
|
112
|
+
*/
|
|
113
|
+
export const scaledDivision = (
|
|
114
|
+
numerator: bigint,
|
|
115
|
+
denominator: bigint,
|
|
116
|
+
precision: number,
|
|
117
|
+
roundingMode: RoundingMode = RoundingMode.HALF_EVEN
|
|
118
|
+
): bigint => {
|
|
119
|
+
if (denominator === 0n) {
|
|
120
|
+
throw new ValidationError('Division by zero');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (precision < 0) {
|
|
124
|
+
throw new ValidationError('Precision must be non-negative');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Scale up numerator to handle desired precision
|
|
128
|
+
const scaledNumerator = scaleByPowerOfTen(numerator, precision);
|
|
129
|
+
const quotient = scaledNumerator / denominator;
|
|
130
|
+
|
|
131
|
+
return round(quotient, 0, roundingMode);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Calculate the number of significant digits
|
|
136
|
+
*/
|
|
137
|
+
export const significantDigits = (value: bigint): number => {
|
|
138
|
+
const nonZeroPattern = /[1-9]/;
|
|
139
|
+
const str = value.toString();
|
|
140
|
+
const firstSignificant = str.search(nonZeroPattern);
|
|
141
|
+
if (firstSignificant === -1) return 0;
|
|
142
|
+
|
|
143
|
+
const lastSignificant = str.split('').reverse().findIndex(char => char !== '0');
|
|
144
|
+
return str.length - firstSignificant - (lastSignificant === -1 ? 0 : lastSignificant);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Truncate to specified number of significant digits
|
|
149
|
+
*/
|
|
150
|
+
export const truncateToSignificantDigits = (
|
|
151
|
+
value: bigint,
|
|
152
|
+
digits: number,
|
|
153
|
+
roundingMode: RoundingMode = RoundingMode.HALF_EVEN
|
|
154
|
+
): bigint => {
|
|
155
|
+
if (digits <= 0) {
|
|
156
|
+
throw new ValidationError('Number of significant digits must be positive');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const currentDigits = significantDigits(value);
|
|
160
|
+
|
|
161
|
+
if (currentDigits <= digits) {
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const scale = currentDigits - digits;
|
|
166
|
+
return round(value, scale, roundingMode);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Check if two numbers are equal within a specified precision
|
|
171
|
+
*/
|
|
172
|
+
export const equalWithinPrecision = (
|
|
173
|
+
a: bigint,
|
|
174
|
+
b: bigint,
|
|
175
|
+
precision: number
|
|
176
|
+
): boolean => {
|
|
177
|
+
const diff = a - b;
|
|
178
|
+
const tolerance = BigInt(10) ** BigInt(precision);
|
|
179
|
+
return diff.toString().length <= tolerance.toString().length;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get the fractional part of a number at a given precision
|
|
184
|
+
*/
|
|
185
|
+
export const getFractionalPart = (
|
|
186
|
+
value: bigint,
|
|
187
|
+
precision: number
|
|
188
|
+
): bigint => {
|
|
189
|
+
if (precision <= 0) return 0n;
|
|
190
|
+
|
|
191
|
+
const scale = BigInt(10) ** BigInt(precision);
|
|
192
|
+
return value % scale;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Format a number with exact precision (no rounding)
|
|
197
|
+
*/
|
|
198
|
+
export const toExactPrecision = (value: bigint, precision: number): string => {
|
|
199
|
+
if (precision < 0) {
|
|
200
|
+
throw new ValidationError('Precision must be non-negative');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
let str = value.toString();
|
|
204
|
+
const isNegative = str.startsWith('-');
|
|
205
|
+
if (isNegative) {
|
|
206
|
+
str = str.slice(1);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
while (str.length <= precision) {
|
|
210
|
+
str = '0' + str;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const integerPart = str.slice(0, -precision) || '0';
|
|
214
|
+
const fractionalPart = str.slice(-precision);
|
|
215
|
+
|
|
216
|
+
return `${isNegative ? '-' : ''}${integerPart}.${fractionalPart}`;
|
|
217
217
|
};
|