@fulcro/types 0.1.0
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 +15 -0
- package/README.md +66 -0
- package/dist/bigInteger/index.d.ts +27 -0
- package/dist/bigInteger/index.js +76 -0
- package/dist/brand/index.d.ts +25 -0
- package/dist/brand/index.js +2 -0
- package/dist/decimal/arithmetic.d.ts +99 -0
- package/dist/decimal/arithmetic.js +388 -0
- package/dist/decimal/format.d.ts +55 -0
- package/dist/decimal/format.js +195 -0
- package/dist/decimal/index.d.ts +296 -0
- package/dist/decimal/index.js +407 -0
- package/dist/decimal/parse.d.ts +15 -0
- package/dist/decimal/parse.js +80 -0
- package/dist/decimal/parts.d.ts +83 -0
- package/dist/decimal/parts.js +103 -0
- package/dist/decimal/round.d.ts +32 -0
- package/dist/decimal/round.js +132 -0
- package/dist/doublePrecisionFloat/index.d.ts +19 -0
- package/dist/doublePrecisionFloat/index.js +12 -0
- package/dist/float/index.d.ts +25 -0
- package/dist/float/index.js +61 -0
- package/dist/halfPrecisionFloat/index.d.ts +20 -0
- package/dist/halfPrecisionFloat/index.js +63 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +29 -0
- package/dist/integer/index.d.ts +163 -0
- package/dist/integer/index.js +402 -0
- package/dist/languageService/index.d.ts +21 -0
- package/dist/languageService/index.js +23 -0
- package/dist/layout/index.d.ts +33 -0
- package/dist/layout/index.js +2 -0
- package/dist/numericType/index.d.ts +174 -0
- package/dist/numericType/index.js +2 -0
- package/dist/roundingMode/index.d.ts +30 -0
- package/dist/roundingMode/index.js +29 -0
- package/dist/signedInteger/index.d.ts +40 -0
- package/dist/signedInteger/index.js +34 -0
- package/dist/singlePrecisionFloat/index.d.ts +20 -0
- package/dist/singlePrecisionFloat/index.js +15 -0
- package/dist/transformer/classify/index.d.ts +39 -0
- package/dist/transformer/classify/index.js +84 -0
- package/dist/transformer/index.d.ts +27 -0
- package/dist/transformer/index.js +30 -0
- package/dist/transformer/rewriter/index.d.ts +3 -0
- package/dist/transformer/rewriter/index.js +374 -0
- package/dist/unplugin/index.d.mts +15 -0
- package/dist/unplugin/index.mjs +31 -0
- package/dist/unsignedInteger/index.d.ts +36 -0
- package/dist/unsignedInteger/index.js +34 -0
- package/package.json +71 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.roundParts = exports.powerParts = exports.compareParts = exports.remainderParts = exports.divideParts = exports.multiplyParts = exports.addParts = void 0;
|
|
4
|
+
const parts_1 = require("./parts");
|
|
5
|
+
const round_1 = require("./round");
|
|
6
|
+
/**
|
|
7
|
+
* The operations of `Decimal`, on parts.
|
|
8
|
+
*
|
|
9
|
+
* Each one computes an exact result on integers and hands it to `finish`, which
|
|
10
|
+
* rounds it into the format once. The care is in keeping "exact" affordable: a
|
|
11
|
+
* decimal128 exponent spans more than twelve thousand powers of ten, and an
|
|
12
|
+
* operation that aligned two operands by multiplying one of them out would
|
|
13
|
+
* build a twelve-thousand-digit integer to add 1 to 10^6000. None of the
|
|
14
|
+
* operations below lets an intermediate grow past about a hundred digits,
|
|
15
|
+
* whatever the exponents, and the performance suite holds them to it.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Adds two values.
|
|
19
|
+
*
|
|
20
|
+
* @param left First operand.
|
|
21
|
+
* @param right Second operand.
|
|
22
|
+
* @param mode Rounding mode.
|
|
23
|
+
* @returns The rounded sum.
|
|
24
|
+
*/
|
|
25
|
+
const addParts = (left, right, mode) => {
|
|
26
|
+
if (left.kind === 'nan' || right.kind === 'nan')
|
|
27
|
+
return parts_1.NOT_A_NUMBER;
|
|
28
|
+
if (left.kind === 'infinity' || right.kind === 'infinity') {
|
|
29
|
+
if (left.kind === 'infinity' && right.kind === 'infinity') {
|
|
30
|
+
return left.negative === right.negative ? left : parts_1.NOT_A_NUMBER;
|
|
31
|
+
}
|
|
32
|
+
return left.kind === 'infinity' ? left : right;
|
|
33
|
+
}
|
|
34
|
+
if ((0, parts_1.isZero)(left) && (0, parts_1.isZero)(right)) {
|
|
35
|
+
// IEEE 754: a sum of zeros is negative only when both are, or, for zeros
|
|
36
|
+
// of opposite sign, when rounding towards negative infinity.
|
|
37
|
+
const negative = left.negative === right.negative ? left.negative : mode === 'floor';
|
|
38
|
+
return (0, parts_1.zero)(negative);
|
|
39
|
+
}
|
|
40
|
+
if ((0, parts_1.isZero)(left))
|
|
41
|
+
return right;
|
|
42
|
+
if ((0, parts_1.isZero)(right))
|
|
43
|
+
return left;
|
|
44
|
+
// The larger operand in magnitude of exponent goes first; it decides where
|
|
45
|
+
// the result's last digit can fall.
|
|
46
|
+
const [larger, smaller] = (0, parts_1.adjustedExponent)(left) >= (0, parts_1.adjustedExponent)(right)
|
|
47
|
+
? [left, right]
|
|
48
|
+
: [right, left];
|
|
49
|
+
// Exponent of the thirty-fourth digit of the larger operand.
|
|
50
|
+
const lastPlace = (0, parts_1.adjustedExponent)(larger) - (parts_1.PRECISION - 1);
|
|
51
|
+
// An operand below a hundredth of that place can only decide the rounding,
|
|
52
|
+
// never a digit. It is replaced by the smallest non-zero value three places
|
|
53
|
+
// below it, which rounds every way it would have — up, down, or past a
|
|
54
|
+
// borrow — and keeps the alignment below to a few dozen digits instead of
|
|
55
|
+
// one per power of ten between the two.
|
|
56
|
+
const addend = (0, parts_1.adjustedExponent)(smaller) < lastPlace - 2
|
|
57
|
+
? { ...smaller, coefficient: 1n, exponent: lastPlace - 3 }
|
|
58
|
+
: smaller;
|
|
59
|
+
const exponent = Math.min(larger.exponent, addend.exponent);
|
|
60
|
+
const signed = (parts) => {
|
|
61
|
+
const magnitude = parts.coefficient * (0, parts_1.powerOfTen)(parts.exponent - exponent);
|
|
62
|
+
return parts.negative ? -magnitude : magnitude;
|
|
63
|
+
};
|
|
64
|
+
const sum = signed(larger) + signed(addend);
|
|
65
|
+
// An exact cancellation is +0, except when rounding towards negative
|
|
66
|
+
// infinity, where it is -0.
|
|
67
|
+
if (sum === 0n)
|
|
68
|
+
return (0, parts_1.zero)(mode === 'floor');
|
|
69
|
+
return (0, round_1.finish)(sum < 0n, sum < 0n ? -sum : sum, exponent, mode);
|
|
70
|
+
};
|
|
71
|
+
exports.addParts = addParts;
|
|
72
|
+
/**
|
|
73
|
+
* Multiplies two values.
|
|
74
|
+
*
|
|
75
|
+
* The product of two coefficients has at most sixty-eight digits, and the
|
|
76
|
+
* exponents simply add, so nothing here needs guarding.
|
|
77
|
+
*
|
|
78
|
+
* @param left First operand.
|
|
79
|
+
* @param right Second operand.
|
|
80
|
+
* @param mode Rounding mode.
|
|
81
|
+
* @returns The rounded product.
|
|
82
|
+
*/
|
|
83
|
+
const multiplyParts = (left, right, mode) => {
|
|
84
|
+
const negative = left.negative !== right.negative;
|
|
85
|
+
if (left.kind === 'nan' || right.kind === 'nan')
|
|
86
|
+
return parts_1.NOT_A_NUMBER;
|
|
87
|
+
if (left.kind === 'infinity' || right.kind === 'infinity') {
|
|
88
|
+
return (0, parts_1.isZero)(left) || (0, parts_1.isZero)(right) ? parts_1.NOT_A_NUMBER : (0, parts_1.infinity)(negative);
|
|
89
|
+
}
|
|
90
|
+
return (0, round_1.finish)(negative, left.coefficient * right.coefficient, left.exponent + right.exponent, mode);
|
|
91
|
+
};
|
|
92
|
+
exports.multiplyParts = multiplyParts;
|
|
93
|
+
/**
|
|
94
|
+
* Divides one value by another.
|
|
95
|
+
*
|
|
96
|
+
* The dividend is scaled so that the quotient has thirty-five digits — one
|
|
97
|
+
* more than the format keeps — and a non-zero remainder is recorded as a
|
|
98
|
+
* thirty-sixth digit of 1. Those two digits are all the rounding reads, so the
|
|
99
|
+
* quotient is rounded exactly as the infinitely precise one would be.
|
|
100
|
+
*
|
|
101
|
+
* Division by zero follows IEEE 754 rather than throwing: a non-zero value over
|
|
102
|
+
* zero is an infinity, and zero over zero is not a number.
|
|
103
|
+
*
|
|
104
|
+
* @param left Dividend.
|
|
105
|
+
* @param right Divisor.
|
|
106
|
+
* @param mode Rounding mode.
|
|
107
|
+
* @returns The rounded quotient.
|
|
108
|
+
*/
|
|
109
|
+
const divideParts = (left, right, mode) => {
|
|
110
|
+
const negative = left.negative !== right.negative;
|
|
111
|
+
if (left.kind === 'nan' || right.kind === 'nan')
|
|
112
|
+
return parts_1.NOT_A_NUMBER;
|
|
113
|
+
if (left.kind === 'infinity') {
|
|
114
|
+
return right.kind === 'infinity' ? parts_1.NOT_A_NUMBER : (0, parts_1.infinity)(negative);
|
|
115
|
+
}
|
|
116
|
+
if (right.kind === 'infinity')
|
|
117
|
+
return (0, parts_1.zero)(negative);
|
|
118
|
+
if ((0, parts_1.isZero)(right))
|
|
119
|
+
return (0, parts_1.isZero)(left) ? parts_1.NOT_A_NUMBER : (0, parts_1.infinity)(negative);
|
|
120
|
+
if ((0, parts_1.isZero)(left))
|
|
121
|
+
return (0, parts_1.zero)(negative);
|
|
122
|
+
const shift = Math.max(0, parts_1.PRECISION +
|
|
123
|
+
1 +
|
|
124
|
+
(0, parts_1.digitCount)(right.coefficient) -
|
|
125
|
+
(0, parts_1.digitCount)(left.coefficient));
|
|
126
|
+
const dividend = left.coefficient * (0, parts_1.powerOfTen)(shift);
|
|
127
|
+
const quotient = dividend / right.coefficient;
|
|
128
|
+
const exponent = left.exponent - right.exponent - shift;
|
|
129
|
+
if (dividend % right.coefficient === 0n) {
|
|
130
|
+
return (0, round_1.finish)(negative, quotient, exponent, mode);
|
|
131
|
+
}
|
|
132
|
+
return (0, round_1.finish)(negative, quotient * 10n + 1n, exponent - 1, mode);
|
|
133
|
+
};
|
|
134
|
+
exports.divideParts = divideParts;
|
|
135
|
+
/**
|
|
136
|
+
* 10^exponent modulo a value, by repeated squaring.
|
|
137
|
+
*
|
|
138
|
+
* What lets the remainder of 1 × 10^6000 by 7 be found without writing out
|
|
139
|
+
* 10^6000: thirteen squarings of numbers smaller than the divisor.
|
|
140
|
+
*
|
|
141
|
+
* @param exponent Non-negative power of ten.
|
|
142
|
+
* @param modulus Positive modulus.
|
|
143
|
+
* @returns 10^exponent mod modulus.
|
|
144
|
+
*/
|
|
145
|
+
const powerOfTenModulo = (exponent, modulus) => {
|
|
146
|
+
let result = 1n % modulus;
|
|
147
|
+
let base = 10n % modulus;
|
|
148
|
+
let remaining = exponent;
|
|
149
|
+
while (remaining > 0) {
|
|
150
|
+
if (remaining % 2 === 1)
|
|
151
|
+
result = (result * base) % modulus;
|
|
152
|
+
base = (base * base) % modulus;
|
|
153
|
+
remaining = Math.floor(remaining / 2);
|
|
154
|
+
}
|
|
155
|
+
return result;
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* The remainder of a division truncated towards zero, carrying the sign of the
|
|
159
|
+
* dividend — what `%` computes on numbers.
|
|
160
|
+
*
|
|
161
|
+
* Always exact: the remainder is smaller than the divisor and no finer than the
|
|
162
|
+
* finer of the two operands, so it always fits the format.
|
|
163
|
+
*
|
|
164
|
+
* @param left Dividend.
|
|
165
|
+
* @param right Divisor.
|
|
166
|
+
* @returns The remainder.
|
|
167
|
+
*/
|
|
168
|
+
const remainderParts = (left, right) => {
|
|
169
|
+
if (left.kind === 'nan' || right.kind === 'nan')
|
|
170
|
+
return parts_1.NOT_A_NUMBER;
|
|
171
|
+
if (left.kind === 'infinity' || (0, parts_1.isZero)(right))
|
|
172
|
+
return parts_1.NOT_A_NUMBER;
|
|
173
|
+
if (right.kind === 'infinity' || (0, parts_1.isZero)(left))
|
|
174
|
+
return left;
|
|
175
|
+
// A dividend smaller than the divisor is its own remainder.
|
|
176
|
+
if ((0, parts_1.adjustedExponent)(left) < (0, parts_1.adjustedExponent)(right))
|
|
177
|
+
return left;
|
|
178
|
+
let remainder;
|
|
179
|
+
let exponent;
|
|
180
|
+
if (left.exponent >= right.exponent) {
|
|
181
|
+
// The dividend is coefficient × 10^gap in units of the divisor's last
|
|
182
|
+
// place, and the gap can be twelve thousand: reduce the power instead.
|
|
183
|
+
const gap = left.exponent - right.exponent;
|
|
184
|
+
remainder =
|
|
185
|
+
(left.coefficient * powerOfTenModulo(gap, right.coefficient)) %
|
|
186
|
+
right.coefficient;
|
|
187
|
+
exponent = right.exponent;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
// The divisor's last place is the coarser one. Since the dividend is at
|
|
191
|
+
// least as large, the gap is under thirty-four places.
|
|
192
|
+
remainder =
|
|
193
|
+
left.coefficient %
|
|
194
|
+
(right.coefficient * (0, parts_1.powerOfTen)(right.exponent - left.exponent));
|
|
195
|
+
exponent = left.exponent;
|
|
196
|
+
}
|
|
197
|
+
if (remainder === 0n)
|
|
198
|
+
return (0, parts_1.zero)(left.negative);
|
|
199
|
+
return (0, round_1.finish)(left.negative, remainder, exponent, 'halfEven');
|
|
200
|
+
};
|
|
201
|
+
exports.remainderParts = remainderParts;
|
|
202
|
+
/**
|
|
203
|
+
* Compares two values in magnitude and sign.
|
|
204
|
+
*
|
|
205
|
+
* @param left First operand.
|
|
206
|
+
* @param right Second operand.
|
|
207
|
+
* @returns -1, 0 or 1, or `undefined` when either is not a number, which is
|
|
208
|
+
* ordered against nothing.
|
|
209
|
+
*/
|
|
210
|
+
const compareParts = (left, right) => {
|
|
211
|
+
if (left.kind === 'nan' || right.kind === 'nan')
|
|
212
|
+
return undefined;
|
|
213
|
+
// Both zeros are equal, whatever their signs.
|
|
214
|
+
if ((0, parts_1.isZero)(left) && (0, parts_1.isZero)(right))
|
|
215
|
+
return 0;
|
|
216
|
+
const sign = (parts) => (0, parts_1.isZero)(parts) ? 0 : parts.negative ? -1 : 1;
|
|
217
|
+
if (sign(left) !== sign(right))
|
|
218
|
+
return sign(left) < sign(right) ? -1 : 1;
|
|
219
|
+
const direction = left.negative ? -1 : 1;
|
|
220
|
+
if (left.kind === 'infinity' || right.kind === 'infinity') {
|
|
221
|
+
if (left.kind === right.kind)
|
|
222
|
+
return 0;
|
|
223
|
+
return left.kind === 'infinity' ? direction : -direction;
|
|
224
|
+
}
|
|
225
|
+
const leftAdjusted = (0, parts_1.adjustedExponent)(left);
|
|
226
|
+
const rightAdjusted = (0, parts_1.adjustedExponent)(right);
|
|
227
|
+
if (leftAdjusted !== rightAdjusted) {
|
|
228
|
+
return leftAdjusted > rightAdjusted ? direction : -direction;
|
|
229
|
+
}
|
|
230
|
+
// Same leading exponent, so the alignment is under thirty-four places.
|
|
231
|
+
const exponent = Math.min(left.exponent, right.exponent);
|
|
232
|
+
const leftAligned = left.coefficient * (0, parts_1.powerOfTen)(left.exponent - exponent);
|
|
233
|
+
const rightAligned = right.coefficient * (0, parts_1.powerOfTen)(right.exponent - exponent);
|
|
234
|
+
if (leftAligned === rightAligned)
|
|
235
|
+
return 0;
|
|
236
|
+
return leftAligned > rightAligned ? direction : -direction;
|
|
237
|
+
};
|
|
238
|
+
exports.compareParts = compareParts;
|
|
239
|
+
/** One, the result of any value raised to the power zero. */
|
|
240
|
+
const ONE = {
|
|
241
|
+
kind: 'finite',
|
|
242
|
+
negative: false,
|
|
243
|
+
coefficient: 1n,
|
|
244
|
+
exponent: 0,
|
|
245
|
+
};
|
|
246
|
+
/**
|
|
247
|
+
* The largest exact power computed, in digits. The exact power of a
|
|
248
|
+
* thirty-four-digit coefficient to `n` has `34n` digits; below this the power is
|
|
249
|
+
* computed exactly and rounded once, above it — a base within a hair of one
|
|
250
|
+
* raised to an enormous exponent, the one way to get here without overflowing —
|
|
251
|
+
* with {@link GUARD_DIGITS} digits more than the format keeps.
|
|
252
|
+
*/
|
|
253
|
+
const EXACT_DIGIT_LIMIT = 200_000;
|
|
254
|
+
/** Digits carried beyond the format's thirty-four when a power is not exact. */
|
|
255
|
+
const GUARD_DIGITS = 50;
|
|
256
|
+
/**
|
|
257
|
+
* The decimal logarithm of a finite, non-zero magnitude, as a double.
|
|
258
|
+
*
|
|
259
|
+
* Only ever used to rule a power out as an overflow or an underflow before it is
|
|
260
|
+
* computed, with a margin of whole powers of ten around the limits, so the
|
|
261
|
+
* double's own error never decides anything.
|
|
262
|
+
*
|
|
263
|
+
* @param parts Magnitude to measure.
|
|
264
|
+
* @returns log10 of the magnitude.
|
|
265
|
+
*/
|
|
266
|
+
const decimalLogarithm = (parts) => {
|
|
267
|
+
const digits = parts.coefficient.toString();
|
|
268
|
+
const leading = digits.slice(0, 15);
|
|
269
|
+
return (parts.exponent +
|
|
270
|
+
Math.log10(Number(leading)) +
|
|
271
|
+
(digits.length - leading.length));
|
|
272
|
+
};
|
|
273
|
+
/**
|
|
274
|
+
* A positive integer power computed with a bounded number of digits: every
|
|
275
|
+
* product is cut back to {@link GUARD_DIGITS} beyond the format, and a digit
|
|
276
|
+
* of 1 is kept below them when anything non-zero was cut.
|
|
277
|
+
*
|
|
278
|
+
* @param coefficient Coefficient of the base.
|
|
279
|
+
* @param exponent Exponent of the base.
|
|
280
|
+
* @param power Exponent it is raised to, positive.
|
|
281
|
+
* @returns The coefficient and exponent of the power, the last digit sticky.
|
|
282
|
+
*/
|
|
283
|
+
const guardedPower = (coefficient, exponent, power) => {
|
|
284
|
+
const kept = parts_1.PRECISION + GUARD_DIGITS;
|
|
285
|
+
let inexact = false;
|
|
286
|
+
const cut = (value, scale) => {
|
|
287
|
+
const excess = (0, parts_1.digitCount)(value) - kept;
|
|
288
|
+
if (excess <= 0)
|
|
289
|
+
return [value, scale];
|
|
290
|
+
const divisor = (0, parts_1.powerOfTen)(excess);
|
|
291
|
+
if (value % divisor !== 0n)
|
|
292
|
+
inexact = true;
|
|
293
|
+
return [value / divisor, scale + excess];
|
|
294
|
+
};
|
|
295
|
+
let result = [1n, 0];
|
|
296
|
+
let factor = [coefficient, exponent];
|
|
297
|
+
let remaining = power;
|
|
298
|
+
while (remaining > 0n) {
|
|
299
|
+
if (remaining % 2n === 1n) {
|
|
300
|
+
result = cut(result[0] * factor[0], result[1] + factor[1]);
|
|
301
|
+
}
|
|
302
|
+
remaining /= 2n;
|
|
303
|
+
if (remaining > 0n) {
|
|
304
|
+
factor = cut(factor[0] * factor[0], factor[1] * 2);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return inexact
|
|
308
|
+
? { coefficient: result[0] * 10n + 1n, exponent: result[1] - 1 }
|
|
309
|
+
: { coefficient: result[0], exponent: result[1] };
|
|
310
|
+
};
|
|
311
|
+
/**
|
|
312
|
+
* Raises a value to an integer power, with the special cases of IEEE 754's
|
|
313
|
+
* `pown`: anything to the power zero is one, `NaN` included; a zero or an
|
|
314
|
+
* infinity to a negative power swaps with the other; and the sign is negative
|
|
315
|
+
* only for a negative base and an odd power.
|
|
316
|
+
*
|
|
317
|
+
* The finite case is computed exactly and rounded once, so the result is the
|
|
318
|
+
* correctly rounded one — up to {@link EXACT_DIGIT_LIMIT} digits of exact
|
|
319
|
+
* power, past which it carries {@link GUARD_DIGITS} guard digits instead. A
|
|
320
|
+
* result that plainly overflows or underflows is settled from its logarithm,
|
|
321
|
+
* without building a number only to discard it.
|
|
322
|
+
*
|
|
323
|
+
* @param base Value raised.
|
|
324
|
+
* @param power Integer exponent.
|
|
325
|
+
* @param mode Rounding mode.
|
|
326
|
+
* @returns The rounded power.
|
|
327
|
+
*/
|
|
328
|
+
const powerParts = (base, power, mode) => {
|
|
329
|
+
if (power === 0n)
|
|
330
|
+
return ONE;
|
|
331
|
+
if (base.kind === 'nan')
|
|
332
|
+
return parts_1.NOT_A_NUMBER;
|
|
333
|
+
const negative = base.negative && power % 2n !== 0n;
|
|
334
|
+
const positive = power > 0n;
|
|
335
|
+
if (base.kind === 'infinity') {
|
|
336
|
+
return positive ? (0, parts_1.infinity)(negative) : (0, parts_1.zero)(negative);
|
|
337
|
+
}
|
|
338
|
+
if ((0, parts_1.isZero)(base))
|
|
339
|
+
return positive ? (0, parts_1.zero)(negative) : (0, parts_1.infinity)(negative);
|
|
340
|
+
if (base.coefficient === 1n && base.exponent === 0) {
|
|
341
|
+
return { ...ONE, negative };
|
|
342
|
+
}
|
|
343
|
+
const magnitude = power < 0n ? -power : power;
|
|
344
|
+
const logarithm = decimalLogarithm(base) * Number(power);
|
|
345
|
+
// Two whole powers of ten of margin either side: the double estimate is off
|
|
346
|
+
// by far less, and anything inside the margin is computed.
|
|
347
|
+
if (logarithm > parts_1.MAXIMUM_ADJUSTED_EXPONENT + 2) {
|
|
348
|
+
return (0, round_1.finish)(negative, 1n, parts_1.MAXIMUM_ADJUSTED_EXPONENT + 1, mode);
|
|
349
|
+
}
|
|
350
|
+
if (logarithm < parts_1.MINIMUM_EXPONENT - 2) {
|
|
351
|
+
return (0, round_1.finish)(negative, 1n, parts_1.MINIMUM_EXPONENT - 2, mode);
|
|
352
|
+
}
|
|
353
|
+
const exact = magnitude * BigInt((0, parts_1.digitCount)(base.coefficient)) <=
|
|
354
|
+
BigInt(EXACT_DIGIT_LIMIT);
|
|
355
|
+
const raised = exact
|
|
356
|
+
? {
|
|
357
|
+
coefficient: base.coefficient ** magnitude,
|
|
358
|
+
exponent: base.exponent * Number(magnitude),
|
|
359
|
+
}
|
|
360
|
+
: guardedPower(base.coefficient, base.exponent, magnitude);
|
|
361
|
+
if (positive) {
|
|
362
|
+
return (0, round_1.finish)(negative, raised.coefficient, raised.exponent, mode);
|
|
363
|
+
}
|
|
364
|
+
return (0, exports.divideParts)(ONE, {
|
|
365
|
+
kind: 'finite',
|
|
366
|
+
negative,
|
|
367
|
+
coefficient: raised.coefficient,
|
|
368
|
+
exponent: raised.exponent,
|
|
369
|
+
}, mode);
|
|
370
|
+
};
|
|
371
|
+
exports.powerParts = powerParts;
|
|
372
|
+
/**
|
|
373
|
+
* Rounds a value to a number of places after the point.
|
|
374
|
+
*
|
|
375
|
+
* @param parts Value to round.
|
|
376
|
+
* @param places Places after the point; negative to round to tens, hundreds…
|
|
377
|
+
* @param mode Rounding mode.
|
|
378
|
+
* @returns The rounded value.
|
|
379
|
+
*/
|
|
380
|
+
const roundParts = (parts, places, mode) => {
|
|
381
|
+
if (parts.kind !== 'finite' || (0, parts_1.isZero)(parts))
|
|
382
|
+
return parts;
|
|
383
|
+
if (parts.exponent >= -places)
|
|
384
|
+
return parts;
|
|
385
|
+
const coefficient = (0, round_1.discardDigits)(parts.coefficient, -places - parts.exponent, mode, parts.negative);
|
|
386
|
+
return (0, round_1.finish)(parts.negative, coefficient, -places, mode);
|
|
387
|
+
};
|
|
388
|
+
exports.roundParts = roundParts;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { RoundingMode } from '../roundingMode/index.js';
|
|
2
|
+
import { type DecimalParts } from './parts';
|
|
3
|
+
/**
|
|
4
|
+
* Refuses a digit count outside what the `Number` methods accept.
|
|
5
|
+
*
|
|
6
|
+
* @param operation Method being called.
|
|
7
|
+
* @param digits Count it was handed.
|
|
8
|
+
* @param minimum Smallest count accepted.
|
|
9
|
+
* @returns The count.
|
|
10
|
+
* @throws {RangeError} When the count is not an integer in range.
|
|
11
|
+
*/
|
|
12
|
+
export declare const requireDigits: (operation: string, digits: number, minimum: number) => number;
|
|
13
|
+
/**
|
|
14
|
+
* The shortest text that reads back as the same value.
|
|
15
|
+
*
|
|
16
|
+
* Plain notation from 10^-6 up to 10^21 and exponential outside it, the points
|
|
17
|
+
* where `Number.prototype.toString` switches. A zero keeps its sign, so `-0`
|
|
18
|
+
* prints as `-0` — a decimal can round to it, and it would otherwise print as a
|
|
19
|
+
* value it is not.
|
|
20
|
+
*
|
|
21
|
+
* @param parts Parts to print.
|
|
22
|
+
* @returns The text.
|
|
23
|
+
*/
|
|
24
|
+
export declare const formatDecimal: (parts: DecimalParts) => string;
|
|
25
|
+
/**
|
|
26
|
+
* The value with a fixed number of digits after the point, in plain notation
|
|
27
|
+
* whatever its magnitude.
|
|
28
|
+
*
|
|
29
|
+
* @param parts Parts to print.
|
|
30
|
+
* @param fractionDigits Digits after the point.
|
|
31
|
+
* @param mode Rounding mode.
|
|
32
|
+
* @returns The text.
|
|
33
|
+
*/
|
|
34
|
+
export declare const formatFixed: (parts: DecimalParts, fractionDigits: number, mode: RoundingMode) => string;
|
|
35
|
+
/**
|
|
36
|
+
* The value with a number of significant digits, in plain notation unless the
|
|
37
|
+
* exponent is below -6 or at least the precision — the rule of
|
|
38
|
+
* `Number.prototype.toPrecision`.
|
|
39
|
+
*
|
|
40
|
+
* @param parts Parts to print.
|
|
41
|
+
* @param precision Significant digits.
|
|
42
|
+
* @param mode Rounding mode.
|
|
43
|
+
* @returns The text.
|
|
44
|
+
*/
|
|
45
|
+
export declare const formatPrecision: (parts: DecimalParts, precision: number, mode: RoundingMode) => string;
|
|
46
|
+
/**
|
|
47
|
+
* The value in exponential notation, with a number of digits after the point,
|
|
48
|
+
* or with as many as it takes when none is given.
|
|
49
|
+
*
|
|
50
|
+
* @param parts Parts to print.
|
|
51
|
+
* @param fractionDigits Digits after the point, or `undefined` for all of them.
|
|
52
|
+
* @param mode Rounding mode.
|
|
53
|
+
* @returns The text.
|
|
54
|
+
*/
|
|
55
|
+
export declare const formatExponential: (parts: DecimalParts, fractionDigits: number | undefined, mode: RoundingMode) => string;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatExponential = exports.formatPrecision = exports.formatFixed = exports.formatDecimal = exports.requireDigits = void 0;
|
|
4
|
+
const parts_1 = require("./parts");
|
|
5
|
+
const round_1 = require("./round");
|
|
6
|
+
/**
|
|
7
|
+
* The textual forms of a decimal, each following the method of `Number` it is
|
|
8
|
+
* named after — the same switch points between plain and exponential notation,
|
|
9
|
+
* the same argument ranges — so a reader who knows one knows the other.
|
|
10
|
+
*/
|
|
11
|
+
/** Largest digit count `toFixed`, `toPrecision` and `toExponential` accept. */
|
|
12
|
+
const MAXIMUM_DIGITS = 100;
|
|
13
|
+
/**
|
|
14
|
+
* Refuses a digit count outside what the `Number` methods accept.
|
|
15
|
+
*
|
|
16
|
+
* @param operation Method being called.
|
|
17
|
+
* @param digits Count it was handed.
|
|
18
|
+
* @param minimum Smallest count accepted.
|
|
19
|
+
* @returns The count.
|
|
20
|
+
* @throws {RangeError} When the count is not an integer in range.
|
|
21
|
+
*/
|
|
22
|
+
const requireDigits = (operation, digits, minimum) => {
|
|
23
|
+
if (!Number.isInteger(digits) ||
|
|
24
|
+
digits < minimum ||
|
|
25
|
+
digits > MAXIMUM_DIGITS) {
|
|
26
|
+
throw new RangeError(`Decimal.${operation}: expected an integer from ${minimum} to ${MAXIMUM_DIGITS}, received ${digits}.`);
|
|
27
|
+
}
|
|
28
|
+
return digits;
|
|
29
|
+
};
|
|
30
|
+
exports.requireDigits = requireDigits;
|
|
31
|
+
/**
|
|
32
|
+
* The text of a value that is not finite, or `null` for one that is.
|
|
33
|
+
*
|
|
34
|
+
* @param parts Parts to print.
|
|
35
|
+
* @returns `'NaN'`, `'Infinity'`, `'-Infinity'` or `null`.
|
|
36
|
+
*/
|
|
37
|
+
const specialText = (parts) => {
|
|
38
|
+
if (parts.kind === 'nan')
|
|
39
|
+
return 'NaN';
|
|
40
|
+
if (parts.kind === 'infinity') {
|
|
41
|
+
return parts.negative ? '-Infinity' : 'Infinity';
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Places a decimal point in a string of digits.
|
|
47
|
+
*
|
|
48
|
+
* @param digits Digits of the value.
|
|
49
|
+
* @param exponent Power of ten the digits are scaled by.
|
|
50
|
+
* @returns The value in plain notation.
|
|
51
|
+
*/
|
|
52
|
+
const placePoint = (digits, exponent) => {
|
|
53
|
+
if (exponent >= 0)
|
|
54
|
+
return digits + '0'.repeat(exponent);
|
|
55
|
+
const point = digits.length + exponent;
|
|
56
|
+
return point > 0
|
|
57
|
+
? `${digits.slice(0, point)}.${digits.slice(point)}`
|
|
58
|
+
: `0.${'0'.repeat(-point)}${digits}`;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Writes digits in exponential notation, as `Number` does: `1.25e+3`.
|
|
62
|
+
*
|
|
63
|
+
* @param digits Digits of the value, the first one non-zero unless all are.
|
|
64
|
+
* @param adjusted Exponent of the leading digit.
|
|
65
|
+
* @returns The value in exponential notation.
|
|
66
|
+
*/
|
|
67
|
+
const exponential = (digits, adjusted) => `${digits[0]}${digits.length > 1 ? `.${digits.slice(1)}` : ''}e${adjusted < 0 ? '-' : '+'}${Math.abs(adjusted)}`;
|
|
68
|
+
/**
|
|
69
|
+
* The shortest text that reads back as the same value.
|
|
70
|
+
*
|
|
71
|
+
* Plain notation from 10^-6 up to 10^21 and exponential outside it, the points
|
|
72
|
+
* where `Number.prototype.toString` switches. A zero keeps its sign, so `-0`
|
|
73
|
+
* prints as `-0` — a decimal can round to it, and it would otherwise print as a
|
|
74
|
+
* value it is not.
|
|
75
|
+
*
|
|
76
|
+
* @param parts Parts to print.
|
|
77
|
+
* @returns The text.
|
|
78
|
+
*/
|
|
79
|
+
const formatDecimal = (parts) => {
|
|
80
|
+
const special = specialText(parts);
|
|
81
|
+
if (special !== null)
|
|
82
|
+
return special;
|
|
83
|
+
const sign = parts.negative ? '-' : '';
|
|
84
|
+
if ((0, parts_1.isZero)(parts))
|
|
85
|
+
return `${sign}0`;
|
|
86
|
+
const digits = parts.coefficient.toString();
|
|
87
|
+
const adjusted = (0, parts_1.adjustedExponent)(parts);
|
|
88
|
+
return adjusted >= 21 || adjusted < -6
|
|
89
|
+
? sign + exponential(digits, adjusted)
|
|
90
|
+
: sign + placePoint(digits, parts.exponent);
|
|
91
|
+
};
|
|
92
|
+
exports.formatDecimal = formatDecimal;
|
|
93
|
+
/**
|
|
94
|
+
* The value with a fixed number of digits after the point, in plain notation
|
|
95
|
+
* whatever its magnitude.
|
|
96
|
+
*
|
|
97
|
+
* @param parts Parts to print.
|
|
98
|
+
* @param fractionDigits Digits after the point.
|
|
99
|
+
* @param mode Rounding mode.
|
|
100
|
+
* @returns The text.
|
|
101
|
+
*/
|
|
102
|
+
const formatFixed = (parts, fractionDigits, mode) => {
|
|
103
|
+
const special = specialText(parts);
|
|
104
|
+
if (special !== null)
|
|
105
|
+
return special;
|
|
106
|
+
const { negative, coefficient, exponent } = parts;
|
|
107
|
+
// The digits scaled so that the last one is the last one printed.
|
|
108
|
+
const scaled = exponent + fractionDigits >= 0
|
|
109
|
+
? coefficient * (0, parts_1.powerOfTen)(exponent + fractionDigits)
|
|
110
|
+
: (0, round_1.discardDigits)(coefficient, -(exponent + fractionDigits), mode, negative);
|
|
111
|
+
const digits = scaled.toString().padStart(fractionDigits + 1, '0');
|
|
112
|
+
const point = digits.length - fractionDigits;
|
|
113
|
+
const text = fractionDigits === 0
|
|
114
|
+
? digits
|
|
115
|
+
: `${digits.slice(0, point)}.${digits.slice(point)}`;
|
|
116
|
+
// A negative value that rounded to zero still prints its sign, as
|
|
117
|
+
// `(-0.001).toFixed(2)` does; `-0` itself does not, as `(-0).toFixed(2)`
|
|
118
|
+
// does not.
|
|
119
|
+
return negative && !(0, parts_1.isZero)(parts) ? `-${text}` : text;
|
|
120
|
+
};
|
|
121
|
+
exports.formatFixed = formatFixed;
|
|
122
|
+
/**
|
|
123
|
+
* The digits of a value rounded to a number of significant digits, padded with
|
|
124
|
+
* zeros when it has fewer.
|
|
125
|
+
*
|
|
126
|
+
* @param parts Finite, non-zero parts.
|
|
127
|
+
* @param precision Significant digits wanted.
|
|
128
|
+
* @param mode Rounding mode.
|
|
129
|
+
* @returns The digits, and the exponent of the leading one.
|
|
130
|
+
*/
|
|
131
|
+
const significantDigits = (parts, precision, mode) => {
|
|
132
|
+
const { negative, coefficient, exponent } = parts;
|
|
133
|
+
const excess = (0, parts_1.digitCount)(coefficient) - precision;
|
|
134
|
+
if (excess <= 0) {
|
|
135
|
+
return {
|
|
136
|
+
digits: coefficient.toString() + '0'.repeat(-excess),
|
|
137
|
+
adjusted: (0, parts_1.adjustedExponent)(parts),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
let rounded = (0, round_1.discardDigits)(coefficient, excess, mode, negative);
|
|
141
|
+
let scale = exponent + excess;
|
|
142
|
+
// 9.99 to two digits is 10, which is one digit too many and a power of ten.
|
|
143
|
+
if (rounded === (0, parts_1.powerOfTen)(precision)) {
|
|
144
|
+
rounded /= 10n;
|
|
145
|
+
scale += 1;
|
|
146
|
+
}
|
|
147
|
+
return { digits: rounded.toString(), adjusted: scale + precision - 1 };
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* The value with a number of significant digits, in plain notation unless the
|
|
151
|
+
* exponent is below -6 or at least the precision — the rule of
|
|
152
|
+
* `Number.prototype.toPrecision`.
|
|
153
|
+
*
|
|
154
|
+
* @param parts Parts to print.
|
|
155
|
+
* @param precision Significant digits.
|
|
156
|
+
* @param mode Rounding mode.
|
|
157
|
+
* @returns The text.
|
|
158
|
+
*/
|
|
159
|
+
const formatPrecision = (parts, precision, mode) => {
|
|
160
|
+
const special = specialText(parts);
|
|
161
|
+
if (special !== null)
|
|
162
|
+
return special;
|
|
163
|
+
const sign = parts.negative && !(0, parts_1.isZero)(parts) ? '-' : '';
|
|
164
|
+
if ((0, parts_1.isZero)(parts))
|
|
165
|
+
return placePoint('0'.repeat(precision), 1 - precision);
|
|
166
|
+
const { digits, adjusted } = significantDigits(parts, precision, mode);
|
|
167
|
+
return adjusted < -6 || adjusted >= precision
|
|
168
|
+
? sign + exponential(digits, adjusted)
|
|
169
|
+
: sign + placePoint(digits, adjusted - precision + 1);
|
|
170
|
+
};
|
|
171
|
+
exports.formatPrecision = formatPrecision;
|
|
172
|
+
/**
|
|
173
|
+
* The value in exponential notation, with a number of digits after the point,
|
|
174
|
+
* or with as many as it takes when none is given.
|
|
175
|
+
*
|
|
176
|
+
* @param parts Parts to print.
|
|
177
|
+
* @param fractionDigits Digits after the point, or `undefined` for all of them.
|
|
178
|
+
* @param mode Rounding mode.
|
|
179
|
+
* @returns The text.
|
|
180
|
+
*/
|
|
181
|
+
const formatExponential = (parts, fractionDigits, mode) => {
|
|
182
|
+
const special = specialText(parts);
|
|
183
|
+
if (special !== null)
|
|
184
|
+
return special;
|
|
185
|
+
const sign = parts.negative && !(0, parts_1.isZero)(parts) ? '-' : '';
|
|
186
|
+
if ((0, parts_1.isZero)(parts)) {
|
|
187
|
+
return exponential('0'.repeat((fractionDigits ?? 0) + 1), 0);
|
|
188
|
+
}
|
|
189
|
+
const precision = fractionDigits === undefined
|
|
190
|
+
? (0, parts_1.digitCount)(parts.coefficient)
|
|
191
|
+
: fractionDigits + 1;
|
|
192
|
+
const { digits, adjusted } = significantDigits(parts, precision, mode);
|
|
193
|
+
return sign + exponential(digits, adjusted);
|
|
194
|
+
};
|
|
195
|
+
exports.formatExponential = formatExponential;
|