@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.
Files changed (51) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +66 -0
  3. package/dist/bigInteger/index.d.ts +27 -0
  4. package/dist/bigInteger/index.js +76 -0
  5. package/dist/brand/index.d.ts +25 -0
  6. package/dist/brand/index.js +2 -0
  7. package/dist/decimal/arithmetic.d.ts +99 -0
  8. package/dist/decimal/arithmetic.js +388 -0
  9. package/dist/decimal/format.d.ts +55 -0
  10. package/dist/decimal/format.js +195 -0
  11. package/dist/decimal/index.d.ts +296 -0
  12. package/dist/decimal/index.js +407 -0
  13. package/dist/decimal/parse.d.ts +15 -0
  14. package/dist/decimal/parse.js +80 -0
  15. package/dist/decimal/parts.d.ts +83 -0
  16. package/dist/decimal/parts.js +103 -0
  17. package/dist/decimal/round.d.ts +32 -0
  18. package/dist/decimal/round.js +132 -0
  19. package/dist/doublePrecisionFloat/index.d.ts +19 -0
  20. package/dist/doublePrecisionFloat/index.js +12 -0
  21. package/dist/float/index.d.ts +25 -0
  22. package/dist/float/index.js +61 -0
  23. package/dist/halfPrecisionFloat/index.d.ts +20 -0
  24. package/dist/halfPrecisionFloat/index.js +63 -0
  25. package/dist/index.d.ts +22 -0
  26. package/dist/index.js +29 -0
  27. package/dist/integer/index.d.ts +163 -0
  28. package/dist/integer/index.js +402 -0
  29. package/dist/languageService/index.d.ts +21 -0
  30. package/dist/languageService/index.js +23 -0
  31. package/dist/layout/index.d.ts +33 -0
  32. package/dist/layout/index.js +2 -0
  33. package/dist/numericType/index.d.ts +174 -0
  34. package/dist/numericType/index.js +2 -0
  35. package/dist/roundingMode/index.d.ts +30 -0
  36. package/dist/roundingMode/index.js +29 -0
  37. package/dist/signedInteger/index.d.ts +40 -0
  38. package/dist/signedInteger/index.js +34 -0
  39. package/dist/singlePrecisionFloat/index.d.ts +20 -0
  40. package/dist/singlePrecisionFloat/index.js +15 -0
  41. package/dist/transformer/classify/index.d.ts +39 -0
  42. package/dist/transformer/classify/index.js +84 -0
  43. package/dist/transformer/index.d.ts +27 -0
  44. package/dist/transformer/index.js +30 -0
  45. package/dist/transformer/rewriter/index.d.ts +3 -0
  46. package/dist/transformer/rewriter/index.js +374 -0
  47. package/dist/unplugin/index.d.mts +15 -0
  48. package/dist/unplugin/index.mjs +31 -0
  49. package/dist/unsignedInteger/index.d.ts +36 -0
  50. package/dist/unsignedInteger/index.js +34 -0
  51. package/package.json +71 -0
@@ -0,0 +1,15 @@
1
+ import { type DecimalParts } from './parts';
2
+ /**
3
+ * Parses a decimal literal into normalised parts, rounding half to even when it
4
+ * carries more than thirty-four significant digits.
5
+ *
6
+ * Linear in the length of the input. The digits past the thirty-fifth are only
7
+ * scanned for a non-zero one, never converted, because turning a long string
8
+ * into a `bigint` costs more than linear time and the digits would be discarded
9
+ * by the rounding immediately afterwards anyway.
10
+ *
11
+ * @param text Literal to parse.
12
+ * @returns The parts.
13
+ * @throws {SyntaxError} When the text is not a decimal literal.
14
+ */
15
+ export declare const parseDecimal: (text: string) => DecimalParts;
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseDecimal = void 0;
4
+ const parts_1 = require("./parts");
5
+ const round_1 = require("./round");
6
+ /**
7
+ * A decimal literal: an optional sign, digits with at most one point and at
8
+ * least one digit, and an optional exponent. The same grammar as a JavaScript
9
+ * numeric string, less the hexadecimal, octal and binary forms, and without
10
+ * surrounding whitespace.
11
+ */
12
+ const DECIMAL_LITERAL = /^([+-]?)(\d*)(?:\.(\d*))?(?:[eE]([+-]?\d+))?$/;
13
+ /**
14
+ * Exponents beyond this are clamped to it. Any exponent this large already
15
+ * overflows or underflows whatever digits come with it, and clamping keeps the
16
+ * arithmetic on it exact in a `number`.
17
+ */
18
+ const EXPONENT_LIMIT = 1_000_000_000;
19
+ /**
20
+ * Significant digits kept from the input before the rest is summarised: the
21
+ * format's thirty-four, and one more to decide the rounding. A thirty-sixth
22
+ * digit then stands for everything after, as 1 when any of it is non-zero —
23
+ * which is all a rounding can ask of it.
24
+ */
25
+ const KEPT_DIGITS = parts_1.PRECISION + 1;
26
+ /**
27
+ * Describes a rejected input for an error message, without printing a
28
+ * megabyte of it.
29
+ *
30
+ * @param text Input that was rejected.
31
+ * @returns A quoted, shortened copy.
32
+ */
33
+ const describeInput = (text) => JSON.stringify(text.length > 40 ? `${text.slice(0, 40)}…` : text);
34
+ /**
35
+ * Parses a decimal literal into normalised parts, rounding half to even when it
36
+ * carries more than thirty-four significant digits.
37
+ *
38
+ * Linear in the length of the input. The digits past the thirty-fifth are only
39
+ * scanned for a non-zero one, never converted, because turning a long string
40
+ * into a `bigint` costs more than linear time and the digits would be discarded
41
+ * by the rounding immediately afterwards anyway.
42
+ *
43
+ * @param text Literal to parse.
44
+ * @returns The parts.
45
+ * @throws {SyntaxError} When the text is not a decimal literal.
46
+ */
47
+ const parseDecimal = (text) => {
48
+ switch (text) {
49
+ case 'NaN':
50
+ return parts_1.NOT_A_NUMBER;
51
+ case 'Infinity':
52
+ case '+Infinity':
53
+ return (0, parts_1.infinity)(false);
54
+ case '-Infinity':
55
+ return (0, parts_1.infinity)(true);
56
+ }
57
+ const match = DECIMAL_LITERAL.exec(text);
58
+ const [, sign = '', whole = '', fraction = '', exponentText] = match ?? [];
59
+ if (match === null || whole.length + fraction.length === 0) {
60
+ throw new SyntaxError(`Decimal.from: expected a decimal literal, received ${describeInput(text)}.`);
61
+ }
62
+ const negative = sign === '-';
63
+ const digits = whole + fraction;
64
+ let exponent = exponentText === undefined
65
+ ? 0
66
+ : Math.max(-EXPONENT_LIMIT, Math.min(EXPONENT_LIMIT, Number(exponentText)));
67
+ exponent -= fraction.length;
68
+ const leading = digits.search(/[1-9]/);
69
+ if (leading === -1)
70
+ return (0, parts_1.zero)(negative);
71
+ const significant = digits.slice(leading);
72
+ if (significant.length <= KEPT_DIGITS + 1) {
73
+ return (0, round_1.finish)(negative, BigInt(significant), exponent, 'halfEven');
74
+ }
75
+ const sticky = /[1-9]/.test(significant.slice(KEPT_DIGITS))
76
+ ? '1'
77
+ : '0';
78
+ return (0, round_1.finish)(negative, BigInt(significant.slice(0, KEPT_DIGITS) + sticky), exponent + significant.length - (KEPT_DIGITS + 1), 'halfEven');
79
+ };
80
+ exports.parseDecimal = parseDecimal;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * The value a `Decimal` holds, taken apart.
3
+ *
4
+ * Every operation of `Decimal` works on these, and the class only wraps them.
5
+ * They are kept **normalised**: a finite coefficient never ends in a zero
6
+ * unless it is zero, and a zero or a special value has an exponent of zero.
7
+ * That makes equality structural and printing unambiguous, and it is the
8
+ * representation the TC39 proposal's "normalise on the way out" implies — no
9
+ * operation can observe a trailing zero, so none has to carry one.
10
+ */
11
+ /** Which of the three kinds of value the parts describe. */
12
+ export type DecimalKind = 'finite' | 'infinity' | 'nan';
13
+ /** A decimal value: (-1)^negative × coefficient × 10^exponent. */
14
+ export interface DecimalParts {
15
+ /** Whether the value is finite, an infinity, or not a number. */
16
+ readonly kind: DecimalKind;
17
+ /** The sign bit, set for negative values, `-0` and `-Infinity`. */
18
+ readonly negative: boolean;
19
+ /** The significant digits, as an integer with no trailing zero. */
20
+ readonly coefficient: bigint;
21
+ /** Power of ten the coefficient is scaled by. */
22
+ readonly exponent: number;
23
+ }
24
+ /** Significant decimal digits a decimal128 value holds. */
25
+ export declare const PRECISION = 34;
26
+ /**
27
+ * Largest exponent of the leading digit, emax in IEEE 754: the largest finite
28
+ * value is 9.999…9 × 10^6144.
29
+ */
30
+ export declare const MAXIMUM_ADJUSTED_EXPONENT = 6144;
31
+ /**
32
+ * Smallest exponent of the last digit, the quantum of the smallest subnormal:
33
+ * 1 × 10^-6176. Anything finer rounds.
34
+ */
35
+ export declare const MINIMUM_EXPONENT = -6176;
36
+ /** Not a number. Its sign is never observed, so it is always clear. */
37
+ export declare const NOT_A_NUMBER: DecimalParts;
38
+ /**
39
+ * An infinity.
40
+ *
41
+ * @param negative Whether it is negative.
42
+ * @returns The parts.
43
+ */
44
+ export declare const infinity: (negative: boolean) => DecimalParts;
45
+ /**
46
+ * A zero.
47
+ *
48
+ * @param negative Whether it is `-0`.
49
+ * @returns The parts.
50
+ */
51
+ export declare const zero: (negative: boolean) => DecimalParts;
52
+ /**
53
+ * Tells whether finite parts are zero.
54
+ *
55
+ * @param parts Parts to inspect.
56
+ * @returns `true` for `0` and `-0`.
57
+ */
58
+ export declare const isZero: (parts: DecimalParts) => boolean;
59
+ /**
60
+ * 10 to a power.
61
+ *
62
+ * Cached up to what the arithmetic actually uses — about a hundred, since no
63
+ * aligned operand is allowed past that — and computed beyond it.
64
+ *
65
+ * @param exponent Non-negative power.
66
+ * @returns 10^exponent.
67
+ */
68
+ export declare const powerOfTen: (exponent: number) => bigint;
69
+ /**
70
+ * Number of decimal digits in a non-negative integer.
71
+ *
72
+ * @param value Integer to measure.
73
+ * @returns Its digit count, 1 for zero.
74
+ */
75
+ export declare const digitCount: (value: bigint) => number;
76
+ /**
77
+ * Exponent of the leading digit of finite, non-zero parts: 2 for 123, -1 for
78
+ * 0.5. Two values compare in magnitude by this first.
79
+ *
80
+ * @param parts Parts to inspect.
81
+ * @returns The adjusted exponent.
82
+ */
83
+ export declare const adjustedExponent: (parts: DecimalParts) => number;
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ /**
3
+ * The value a `Decimal` holds, taken apart.
4
+ *
5
+ * Every operation of `Decimal` works on these, and the class only wraps them.
6
+ * They are kept **normalised**: a finite coefficient never ends in a zero
7
+ * unless it is zero, and a zero or a special value has an exponent of zero.
8
+ * That makes equality structural and printing unambiguous, and it is the
9
+ * representation the TC39 proposal's "normalise on the way out" implies — no
10
+ * operation can observe a trailing zero, so none has to carry one.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.adjustedExponent = exports.digitCount = exports.powerOfTen = exports.isZero = exports.zero = exports.infinity = exports.NOT_A_NUMBER = exports.MINIMUM_EXPONENT = exports.MAXIMUM_ADJUSTED_EXPONENT = exports.PRECISION = void 0;
14
+ /** Significant decimal digits a decimal128 value holds. */
15
+ exports.PRECISION = 34;
16
+ /**
17
+ * Largest exponent of the leading digit, emax in IEEE 754: the largest finite
18
+ * value is 9.999…9 × 10^6144.
19
+ */
20
+ exports.MAXIMUM_ADJUSTED_EXPONENT = 6144;
21
+ /**
22
+ * Smallest exponent of the last digit, the quantum of the smallest subnormal:
23
+ * 1 × 10^-6176. Anything finer rounds.
24
+ */
25
+ exports.MINIMUM_EXPONENT = -6176;
26
+ /** Not a number. Its sign is never observed, so it is always clear. */
27
+ exports.NOT_A_NUMBER = {
28
+ kind: 'nan',
29
+ negative: false,
30
+ coefficient: 0n,
31
+ exponent: 0,
32
+ };
33
+ /**
34
+ * An infinity.
35
+ *
36
+ * @param negative Whether it is negative.
37
+ * @returns The parts.
38
+ */
39
+ const infinity = (negative) => ({
40
+ kind: 'infinity',
41
+ negative,
42
+ coefficient: 0n,
43
+ exponent: 0,
44
+ });
45
+ exports.infinity = infinity;
46
+ /**
47
+ * A zero.
48
+ *
49
+ * @param negative Whether it is `-0`.
50
+ * @returns The parts.
51
+ */
52
+ const zero = (negative) => ({
53
+ kind: 'finite',
54
+ negative,
55
+ coefficient: 0n,
56
+ exponent: 0,
57
+ });
58
+ exports.zero = zero;
59
+ /**
60
+ * Tells whether finite parts are zero.
61
+ *
62
+ * @param parts Parts to inspect.
63
+ * @returns `true` for `0` and `-0`.
64
+ */
65
+ const isZero = (parts) => parts.kind === 'finite' && parts.coefficient === 0n;
66
+ exports.isZero = isZero;
67
+ /** Powers of ten already computed, since every operation reaches for them. */
68
+ const POWERS_OF_TEN = [1n];
69
+ /**
70
+ * 10 to a power.
71
+ *
72
+ * Cached up to what the arithmetic actually uses — about a hundred, since no
73
+ * aligned operand is allowed past that — and computed beyond it.
74
+ *
75
+ * @param exponent Non-negative power.
76
+ * @returns 10^exponent.
77
+ */
78
+ const powerOfTen = (exponent) => {
79
+ if (exponent > 128)
80
+ return 10n ** BigInt(exponent);
81
+ while (POWERS_OF_TEN.length <= exponent) {
82
+ POWERS_OF_TEN.push(POWERS_OF_TEN[POWERS_OF_TEN.length - 1] * 10n);
83
+ }
84
+ return POWERS_OF_TEN[exponent];
85
+ };
86
+ exports.powerOfTen = powerOfTen;
87
+ /**
88
+ * Number of decimal digits in a non-negative integer.
89
+ *
90
+ * @param value Integer to measure.
91
+ * @returns Its digit count, 1 for zero.
92
+ */
93
+ const digitCount = (value) => value.toString().length;
94
+ exports.digitCount = digitCount;
95
+ /**
96
+ * Exponent of the leading digit of finite, non-zero parts: 2 for 123, -1 for
97
+ * 0.5. Two values compare in magnitude by this first.
98
+ *
99
+ * @param parts Parts to inspect.
100
+ * @returns The adjusted exponent.
101
+ */
102
+ const adjustedExponent = (parts) => parts.exponent + (0, exports.digitCount)(parts.coefficient) - 1;
103
+ exports.adjustedExponent = adjustedExponent;
@@ -0,0 +1,32 @@
1
+ import type { RoundingMode } from '../roundingMode/index.js';
2
+ import { type DecimalParts } from './parts';
3
+ /**
4
+ * Divides a magnitude by 10^digits, rounding the quotient by a mode.
5
+ *
6
+ * The number of digits may be far larger than the magnitude has — rounding
7
+ * 1 × 10^-6000 to two places drops six thousand of them — so that case is
8
+ * answered without ever building the power: every digit is discarded, and
9
+ * together they are below a tenth of a unit.
10
+ *
11
+ * @param magnitude Non-negative integer to shorten.
12
+ * @param digits How many trailing digits to discard, at least one.
13
+ * @param mode Rounding mode.
14
+ * @param negative Sign of the value.
15
+ * @returns The rounded quotient.
16
+ */
17
+ export declare const discardDigits: (magnitude: bigint, digits: number, mode: RoundingMode, negative: boolean) => bigint;
18
+ /**
19
+ * Turns an exact result into a decimal128 value: rounded to thirty-four
20
+ * digits, rounded again if it is finer than the smallest subnormal, stripped
21
+ * of trailing zeros, and sent to infinity if it no longer fits.
22
+ *
23
+ * Every operation ends here, which is what keeps every result inside the format
24
+ * whatever the operation computed on the way.
25
+ *
26
+ * @param negative Sign of the result.
27
+ * @param magnitude Exact magnitude, as an integer.
28
+ * @param exponent Power of ten the magnitude is scaled by.
29
+ * @param mode Rounding mode.
30
+ * @returns The parts of the result.
31
+ */
32
+ export declare const finish: (negative: boolean, magnitude: bigint, exponent: number, mode: RoundingMode) => DecimalParts;
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.finish = exports.discardDigits = void 0;
4
+ const parts_1 = require("./parts");
5
+ /**
6
+ * Settles a truncated quotient according to a mode.
7
+ *
8
+ * @param quotient Magnitude with the discarded digits removed.
9
+ * @param discarded What those digits amounted to.
10
+ * @param mode Rounding mode.
11
+ * @param negative Sign of the value, which the directed modes depend on.
12
+ * @returns The rounded magnitude.
13
+ */
14
+ const settle = (quotient, discarded, mode, negative) => {
15
+ if (discarded === 'nothing')
16
+ return quotient;
17
+ switch (mode) {
18
+ case 'truncate':
19
+ return quotient;
20
+ case 'floor':
21
+ return negative ? quotient + 1n : quotient;
22
+ case 'ceiling':
23
+ return negative ? quotient : quotient + 1n;
24
+ case 'halfAwayFromZero':
25
+ return discarded === 'belowHalf' ? quotient : quotient + 1n;
26
+ case 'halfEven':
27
+ if (discarded === 'belowHalf')
28
+ return quotient;
29
+ if (discarded === 'aboveHalf')
30
+ return quotient + 1n;
31
+ return quotient % 2n === 0n ? quotient : quotient + 1n;
32
+ }
33
+ };
34
+ /**
35
+ * Divides a magnitude by 10^digits, rounding the quotient by a mode.
36
+ *
37
+ * The number of digits may be far larger than the magnitude has — rounding
38
+ * 1 × 10^-6000 to two places drops six thousand of them — so that case is
39
+ * answered without ever building the power: every digit is discarded, and
40
+ * together they are below a tenth of a unit.
41
+ *
42
+ * @param magnitude Non-negative integer to shorten.
43
+ * @param digits How many trailing digits to discard, at least one.
44
+ * @param mode Rounding mode.
45
+ * @param negative Sign of the value.
46
+ * @returns The rounded quotient.
47
+ */
48
+ const discardDigits = (magnitude, digits, mode, negative) => {
49
+ if (digits > (0, parts_1.digitCount)(magnitude)) {
50
+ return settle(0n, magnitude === 0n ? 'nothing' : 'belowHalf', mode, negative);
51
+ }
52
+ const divisor = (0, parts_1.powerOfTen)(digits);
53
+ const quotient = magnitude / divisor;
54
+ const remainder = magnitude % divisor;
55
+ const half = divisor / 2n;
56
+ const discarded = remainder === 0n
57
+ ? 'nothing'
58
+ : remainder < half
59
+ ? 'belowHalf'
60
+ : remainder === half
61
+ ? 'half'
62
+ : 'aboveHalf';
63
+ return settle(quotient, discarded, mode, negative);
64
+ };
65
+ exports.discardDigits = discardDigits;
66
+ /**
67
+ * The result of an overflow, which IEEE 754 makes depend on the mode: the
68
+ * modes that round to nearest go to infinity, and a directed mode goes to
69
+ * infinity only in its own direction, stopping at the largest finite value in
70
+ * the other.
71
+ *
72
+ * @param negative Sign of the value that overflowed.
73
+ * @param mode Rounding mode.
74
+ * @returns The parts of the result.
75
+ */
76
+ const overflow = (negative, mode) => {
77
+ const toInfinity = mode === 'halfEven' ||
78
+ mode === 'halfAwayFromZero' ||
79
+ (mode === 'ceiling' && !negative) ||
80
+ (mode === 'floor' && negative);
81
+ if (toInfinity)
82
+ return (0, parts_1.infinity)(negative);
83
+ // 9.999…9 × 10^6144: thirty-four nines, with the last one at 10^6111.
84
+ return {
85
+ kind: 'finite',
86
+ negative,
87
+ coefficient: (0, parts_1.powerOfTen)(parts_1.PRECISION) - 1n,
88
+ exponent: parts_1.MAXIMUM_ADJUSTED_EXPONENT - parts_1.PRECISION + 1,
89
+ };
90
+ };
91
+ /**
92
+ * Turns an exact result into a decimal128 value: rounded to thirty-four
93
+ * digits, rounded again if it is finer than the smallest subnormal, stripped
94
+ * of trailing zeros, and sent to infinity if it no longer fits.
95
+ *
96
+ * Every operation ends here, which is what keeps every result inside the format
97
+ * whatever the operation computed on the way.
98
+ *
99
+ * @param negative Sign of the result.
100
+ * @param magnitude Exact magnitude, as an integer.
101
+ * @param exponent Power of ten the magnitude is scaled by.
102
+ * @param mode Rounding mode.
103
+ * @returns The parts of the result.
104
+ */
105
+ const finish = (negative, magnitude, exponent, mode) => {
106
+ if (magnitude === 0n)
107
+ return (0, parts_1.zero)(negative);
108
+ let coefficient = magnitude;
109
+ let scale = exponent;
110
+ const excess = Math.max((0, parts_1.digitCount)(coefficient) - parts_1.PRECISION, parts_1.MINIMUM_EXPONENT - scale);
111
+ if (excess > 0) {
112
+ coefficient = (0, exports.discardDigits)(coefficient, excess, mode, negative);
113
+ scale += excess;
114
+ // Rounded away entirely: an underflow keeps its sign.
115
+ if (coefficient === 0n)
116
+ return (0, parts_1.zero)(negative);
117
+ // 99…9 rounded up to 10^34 has one digit too many, and it is a zero.
118
+ if (coefficient === (0, parts_1.powerOfTen)(parts_1.PRECISION)) {
119
+ coefficient /= 10n;
120
+ scale += 1;
121
+ }
122
+ }
123
+ while (coefficient % 10n === 0n) {
124
+ coefficient /= 10n;
125
+ scale += 1;
126
+ }
127
+ if (scale + (0, parts_1.digitCount)(coefficient) - 1 > parts_1.MAXIMUM_ADJUSTED_EXPONENT) {
128
+ return overflow(negative, mode);
129
+ }
130
+ return { kind: 'finite', negative, coefficient, exponent: scale };
131
+ };
132
+ exports.finish = finish;
@@ -0,0 +1,19 @@
1
+ import type { Branded } from '../brand/index.js';
2
+ import type { Layout } from '../layout/index.js';
3
+ import type { BoundedNumericType } from '../numericType/index.js';
4
+ /**
5
+ * An IEEE 754 binary64 value: 53 bits of precision, 11 of exponent.
6
+ *
7
+ * Every JavaScript `number` already is one, so conversion never rounds. The type
8
+ * exists so that a double is named as a choice rather than left as the default
9
+ * nobody chose, and so that it carries a layout like the other formats.
10
+ */
11
+ export type DoublePrecisionFloat = Branded<number, 'DoublePrecisionFloat'> & Layout<8, 8>;
12
+ /**
13
+ * The descriptor of {@link DoublePrecisionFloat}.
14
+ *
15
+ * ```ts
16
+ * DoublePrecisionFloat.from(0.1); // 0.1
17
+ * ```
18
+ */
19
+ export declare const DoublePrecisionFloat: BoundedNumericType<DoublePrecisionFloat, number>;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DoublePrecisionFloat = void 0;
4
+ const float_1 = require("../float/index.js");
5
+ /**
6
+ * The descriptor of {@link DoublePrecisionFloat}.
7
+ *
8
+ * ```ts
9
+ * DoublePrecisionFloat.from(0.1); // 0.1
10
+ * ```
11
+ */
12
+ exports.DoublePrecisionFloat = (0, float_1.createFloatType)('DoublePrecisionFloat', Number.MAX_VALUE, (value) => value);
@@ -0,0 +1,25 @@
1
+ import type { BoundedNumericType } from '../numericType/index.js';
2
+ /**
3
+ * Machinery shared by the three binary floating point types.
4
+ *
5
+ * A float type is fully described by one function: the rounding from a double
6
+ * to its own format. Everything else follows from a result proved by Figueroa
7
+ * (1995): when the wider format has at least 2p + 2 bits of precision, an
8
+ * addition, subtraction, multiplication or division computed in the wider
9
+ * format and then rounded to the narrower one is correctly rounded. A double
10
+ * carries 53 bits; single precision needs 2·24 + 2 = 50 and half precision
11
+ * 2·11 + 2 = 24. So each operation below is the double operation, rounded once.
12
+ *
13
+ * The remainder needs no such argument — it is always exact.
14
+ */
15
+ /**
16
+ * Builds the descriptor of a float format.
17
+ *
18
+ * @param name Name of the type, as it reads in an error message.
19
+ * @param maximum Largest finite value of the format. The smallest is its
20
+ * negation, since a float's range is symmetric about zero.
21
+ * @param round Rounding of a double to the nearest value of the format, ties to
22
+ * even.
23
+ * @returns The descriptor.
24
+ */
25
+ export declare const createFloatType: <T>(name: string, maximum: number, round: (value: number) => number) => BoundedNumericType<T, number>;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createFloatType = void 0;
4
+ /**
5
+ * Machinery shared by the three binary floating point types.
6
+ *
7
+ * A float type is fully described by one function: the rounding from a double
8
+ * to its own format. Everything else follows from a result proved by Figueroa
9
+ * (1995): when the wider format has at least 2p + 2 bits of precision, an
10
+ * addition, subtraction, multiplication or division computed in the wider
11
+ * format and then rounded to the narrower one is correctly rounded. A double
12
+ * carries 53 bits; single precision needs 2·24 + 2 = 50 and half precision
13
+ * 2·11 + 2 = 24. So each operation below is the double operation, rounded once.
14
+ *
15
+ * The remainder needs no such argument — it is always exact.
16
+ */
17
+ /**
18
+ * Builds the descriptor of a float format.
19
+ *
20
+ * @param name Name of the type, as it reads in an error message.
21
+ * @param maximum Largest finite value of the format. The smallest is its
22
+ * negation, since a float's range is symmetric about zero.
23
+ * @param round Rounding of a double to the nearest value of the format, ties to
24
+ * even.
25
+ * @returns The descriptor.
26
+ */
27
+ const createFloatType = (name, maximum, round) => ({
28
+ name,
29
+ minimum: -maximum,
30
+ maximum: maximum,
31
+ from: (value) => {
32
+ // A `bigint` is refused rather than converted: turning it into a double
33
+ // first and then into the format rounds twice, and the second rounding
34
+ // can land on the wrong neighbour.
35
+ if (typeof value !== 'number') {
36
+ throw new TypeError(`${name}.from: expected a number, received ${typeof value}.`);
37
+ }
38
+ return round(value);
39
+ },
40
+ is: (value) => typeof value === 'number' &&
41
+ (Number.isNaN(value) || Object.is(round(value), value)),
42
+ add: (left, right) => round(left + right),
43
+ subtract: (left, right) => round(left - right),
44
+ multiply: (left, right) => round(left * right),
45
+ divide: (left, right) => round(left / right),
46
+ remainder: (left, right) => round(left % right),
47
+ // Unlike the four above, `Math.pow` is not correctly rounded even in double
48
+ // precision, so the result is the platform's double power rounded once into
49
+ // the format: faithful, and not promised to be the nearest.
50
+ power: (base, exponent) => round(base ** exponent),
51
+ // Exact in every format: only the sign bit changes.
52
+ negate: (value) => -value,
53
+ increment: (value) => round(value + 1),
54
+ decrement: (value) => round(value - 1),
55
+ equals: (left, right) => left === right,
56
+ lessThan: (left, right) => left < right,
57
+ lessThanOrEqual: (left, right) => left <= right,
58
+ greaterThan: (left, right) => left > right,
59
+ greaterThanOrEqual: (left, right) => left >= right,
60
+ });
61
+ exports.createFloatType = createFloatType;
@@ -0,0 +1,20 @@
1
+ import type { Branded } from '../brand/index.js';
2
+ import type { Layout } from '../layout/index.js';
3
+ import type { BoundedNumericType } from '../numericType/index.js';
4
+ /**
5
+ * An IEEE 754 binary16 value: 11 bits of precision, 5 of exponent, finite from
6
+ * -65504 to 65504.
7
+ *
8
+ * Carried by a `number` holding a value the format can represent exactly.
9
+ * Converting rounds to the nearest such value, ties to even.
10
+ */
11
+ export type HalfPrecisionFloat = Branded<number, 'HalfPrecisionFloat'> & Layout<2, 2>;
12
+ /**
13
+ * The descriptor of {@link HalfPrecisionFloat}.
14
+ *
15
+ * ```ts
16
+ * HalfPrecisionFloat.from(0.1); // 0.0999755859375
17
+ * HalfPrecisionFloat.from(65520); // Infinity
18
+ * ```
19
+ */
20
+ export declare const HalfPrecisionFloat: BoundedNumericType<HalfPrecisionFloat, number>;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HalfPrecisionFloat = void 0;
4
+ const float_1 = require("../float/index.js");
5
+ /**
6
+ * The point from which a double rounds to infinity rather than to 65504, the
7
+ * largest finite value: halfway to 65536, which is where the next value would be if
8
+ * the exponent had room. Exactly halfway is a tie, and it goes to 65536 —
9
+ * whose significand is even — and therefore to infinity.
10
+ */
11
+ const OVERFLOW_THRESHOLD = 65520;
12
+ /** Exponent of the smallest normal half precision value, 2^-14. */
13
+ const MINIMUM_NORMAL_EXPONENT = -14;
14
+ /** Bits of the significand after the leading one. */
15
+ const FRACTION_BITS = 10;
16
+ /**
17
+ * Rounds a double to the nearest half precision value, ties to even.
18
+ *
19
+ * Written here rather than delegated to `Math.f16round`, which Node 22 — the
20
+ * oldest line this package supports — does not have. The method is the one the
21
+ * format implies: find the spacing of half precision values around the input,
22
+ * which is a power of two, express the input in units of it, and round that to
23
+ * an integer. Dividing by a power of two is exact in a double, so the only
24
+ * rounding is the one being performed.
25
+ *
26
+ * @param value Double to round.
27
+ * @returns The nearest half precision value, as a double.
28
+ */
29
+ const roundToHalfPrecision = (value) => {
30
+ // NaN, both infinities and both zeros are their own half precision value.
31
+ if (!Number.isFinite(value) || value === 0)
32
+ return value;
33
+ const magnitude = Math.abs(value);
34
+ const sign = value < 0 ? -1 : 1;
35
+ if (magnitude >= OVERFLOW_THRESHOLD)
36
+ return sign * Infinity;
37
+ // `Math.log2` is not exact next to a power of two, so the estimate is
38
+ // corrected against the power itself.
39
+ let exponent = Math.floor(Math.log2(magnitude));
40
+ if (2 ** exponent > magnitude)
41
+ exponent--;
42
+ else if (2 ** (exponent + 1) <= magnitude)
43
+ exponent++;
44
+ // Below the normal range the spacing stops shrinking: that is what makes
45
+ // the subnormals evenly spaced at 2^-24.
46
+ const spacing = 2 ** (Math.max(exponent, MINIMUM_NORMAL_EXPONENT) - FRACTION_BITS);
47
+ const units = magnitude / spacing;
48
+ // `Math.round` breaks a tie upwards; a tie on an odd count goes back down,
49
+ // which is what makes it ties to even.
50
+ let rounded = Math.round(units);
51
+ if (rounded - units === 0.5 && rounded % 2 !== 0)
52
+ rounded--;
53
+ return sign * rounded * spacing;
54
+ };
55
+ /**
56
+ * The descriptor of {@link HalfPrecisionFloat}.
57
+ *
58
+ * ```ts
59
+ * HalfPrecisionFloat.from(0.1); // 0.0999755859375
60
+ * HalfPrecisionFloat.from(65520); // Infinity
61
+ * ```
62
+ */
63
+ exports.HalfPrecisionFloat = (0, float_1.createFloatType)('HalfPrecisionFloat', 65504, roundToHalfPrecision);
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Public entry point of the package, matching the `main` and `types` fields of
3
+ * `package.json`.
4
+ *
5
+ * Listed one by one rather than re-exported wholesale, so that adding an export
6
+ * to a module below is never enough on its own to put it in front of consumers.
7
+ * The modules export more than this — `createIntegerType`, the parts and the
8
+ * rounding of `Decimal` — and none of it is a promise.
9
+ *
10
+ * Every type here has a value of the same name beside it. A consumer who wants
11
+ * only the types imports them with `import type` and takes no code at all.
12
+ */
13
+ export { BigInteger } from './bigInteger/index.js';
14
+ export { Decimal } from './decimal/index.js';
15
+ export { DoublePrecisionFloat } from './doublePrecisionFloat/index.js';
16
+ export { HalfPrecisionFloat } from './halfPrecisionFloat/index.js';
17
+ export type { IntegerType, IntegerWidth } from './integer/index.js';
18
+ export type { BoundedNumericType, NumericType } from './numericType/index.js';
19
+ export type { RoundingMode } from './roundingMode/index.js';
20
+ export { SignedInteger } from './signedInteger/index.js';
21
+ export { SinglePrecisionFloat } from './singlePrecisionFloat/index.js';
22
+ export { UnsignedInteger } from './unsignedInteger/index.js';