@raoh/core 0.9.0-dev.15.20261004151401.ge5893e0cc07d

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.
@@ -0,0 +1,148 @@
1
+ // A decimal number that keeps the scale it was written with.
2
+ import { tagOf } from "./copy.js";
3
+ const NUMBER = /^([+-]?)([0-9]+)(?:\.([0-9]*))?(?:[eE]([+-]?[0-9]+))?$|^([+-]?)\.([0-9]+)(?:[eE]([+-]?[0-9]+))?$/;
4
+ const INT32_MIN = -(2 ** 31);
5
+ const INT32_MAX = 2 ** 31 - 1;
6
+ /**
7
+ * A coefficient and a scale: the number coefficient × 10^-scale.
8
+ *
9
+ * Two decimals are the same value only when both are equal, so 1.5 and 1.50 are different values;
10
+ * {@link Decimal.compare} orders them by the number they denote, in which they are equal. The
11
+ * scale is an int32, as the value model's is.
12
+ */
13
+ export class Decimal {
14
+ coefficient;
15
+ scale;
16
+ constructor(coefficient, scale) {
17
+ if (!Number.isInteger(scale) || scale < INT32_MIN || scale > INT32_MAX) {
18
+ throw new RangeError(`a decimal's scale is an int32, not ${scale}`);
19
+ }
20
+ this.coefficient = coefficient;
21
+ this.scale = scale;
22
+ }
23
+ /**
24
+ * The decimal a number written as `[+-]?(digits[.digits]|.digits)([eE][+-]?digits)?` denotes,
25
+ * at the scale it is written with: the digits after the point, less the exponent. `undefined`
26
+ * where the text is not such a number, or its scale is not an int32.
27
+ */
28
+ static parse(text) {
29
+ const read = NUMBER.exec(text);
30
+ if (read === null) {
31
+ return undefined;
32
+ }
33
+ const [, sign1, whole1, fraction1, exponent1, sign2, fraction2, exponent2] = read;
34
+ const sign = sign1 ?? sign2 ?? "";
35
+ const whole = whole1 ?? "";
36
+ const fraction = fraction1 ?? fraction2 ?? "";
37
+ const exponent = exponent1 ?? exponent2;
38
+ const exponentValue = exponent === undefined ? 0 : Number(exponent);
39
+ const scale = fraction.length - exponentValue;
40
+ if (!Number.isSafeInteger(exponentValue) || scale < INT32_MIN || scale > INT32_MAX) {
41
+ return undefined;
42
+ }
43
+ const magnitude = BigInt(whole + fraction);
44
+ return new Decimal(sign === "-" ? -magnitude : magnitude, scale);
45
+ }
46
+ get [Symbol.toStringTag]() {
47
+ return tagOf("Decimal");
48
+ }
49
+ /** The decimal of an integer, at scale 0. */
50
+ static of(value) {
51
+ return new Decimal(BigInt(value), 0);
52
+ }
53
+ /** -1, 0 or 1 as this denotes a number less than, equal to or greater than `other`'s. */
54
+ compare(other) {
55
+ if (this.signum !== other.signum) {
56
+ return this.signum < other.signum ? -1 : 1;
57
+ }
58
+ if (this.signum === 0) {
59
+ return 0;
60
+ }
61
+ // Of two numbers of one sign whose first digits stand at different powers of ten, the one
62
+ // whose first digit stands higher is the larger in magnitude. Settling that first keeps
63
+ // 1E+999999999 and 1E-999999999 from being brought to one scale.
64
+ const above = adjustedExponent(this) - adjustedExponent(other);
65
+ if (above !== 0) {
66
+ return above > 0 === this.signum > 0 ? 1 : -1;
67
+ }
68
+ const [a, b] = aligned(this, other);
69
+ return a < b ? -1 : a > b ? 1 : 0;
70
+ }
71
+ /** Whether the two are the same value: the same coefficient and the same scale. */
72
+ equals(other) {
73
+ return this.coefficient === other.coefficient && this.scale === other.scale;
74
+ }
75
+ /** -1, 0 or 1 as the number is negative, zero or positive. */
76
+ get signum() {
77
+ return this.coefficient < 0n ? -1 : this.coefficient > 0n ? 1 : 0;
78
+ }
79
+ /** Whether this is an integer multiple of `divisor`, which is not zero. */
80
+ isMultipleOf(divisor) {
81
+ if (this.coefficient === 0n) {
82
+ return true;
83
+ }
84
+ // An integer times the divisor has no more digits after the point than the divisor has, once
85
+ // neither carries trailing zeros.
86
+ const value = stripped(this);
87
+ const by = stripped(divisor);
88
+ if (value.scale > by.scale) {
89
+ return false;
90
+ }
91
+ // value / divisor = value's coefficient × 10^n / divisor's, for n the difference of the
92
+ // scales. Past the divisor's own count of 2s and 5s, which its bit length bounds, a further
93
+ // factor of 10 decides nothing, so n is taken no larger than that: 7E-2147483647 does not
94
+ // make a power of ten with two billion digits.
95
+ const divisorDigits = by.coefficient < 0n ? -by.coefficient : by.coefficient;
96
+ const n = Math.min(by.scale - value.scale, divisorDigits.toString(2).length);
97
+ return (value.coefficient * 10n ** BigInt(n)) % by.coefficient === 0n;
98
+ }
99
+ /**
100
+ * The decimal as Java's `BigDecimal.toString` writes it, which is the message form the Raoh
101
+ * Specification gives: plain where the scale is not negative and the first digit's exponent is
102
+ * at least -6, and otherwise one digit before the point and an exponent (`1E+3`, `1.5E-7`).
103
+ */
104
+ toString() {
105
+ const digits = (this.coefficient < 0n ? -this.coefficient : this.coefficient).toString();
106
+ const sign = this.coefficient < 0n ? "-" : "";
107
+ const adjusted = digits.length - 1 - this.scale;
108
+ if (this.scale >= 0 && adjusted >= -6) {
109
+ if (this.scale === 0) {
110
+ return sign + digits;
111
+ }
112
+ if (digits.length > this.scale) {
113
+ const point = digits.length - this.scale;
114
+ return `${sign}${digits.slice(0, point)}.${digits.slice(point)}`;
115
+ }
116
+ return `${sign}0.${"0".repeat(this.scale - digits.length)}${digits}`;
117
+ }
118
+ const mantissa = digits.length > 1 ? `${digits[0]}.${digits.slice(1)}` : digits;
119
+ return `${sign}${mantissa}E${adjusted >= 0 ? "+" : "-"}${Math.abs(adjusted)}`;
120
+ }
121
+ toJSON() {
122
+ return this.toString();
123
+ }
124
+ }
125
+ /** The exponent of the first digit: 2 for 123, -1 for 0.5, 2 for 1.5E+2. */
126
+ function adjustedExponent(decimal) {
127
+ const digits = decimal.coefficient < 0n ? -decimal.coefficient : decimal.coefficient;
128
+ return digits.toString().length - 1 - decimal.scale;
129
+ }
130
+ /** The same number with no trailing zeros in its coefficient. */
131
+ function stripped(decimal) {
132
+ let { coefficient, scale } = decimal;
133
+ while (coefficient !== 0n && coefficient % 10n === 0n) {
134
+ coefficient /= 10n;
135
+ scale -= 1;
136
+ }
137
+ return { coefficient, scale };
138
+ }
139
+ /** The two coefficients brought to the larger scale, so that they compare as the numbers do. */
140
+ function aligned(a, b) {
141
+ if (a.scale === b.scale) {
142
+ return [a.coefficient, b.coefficient];
143
+ }
144
+ if (a.scale > b.scale) {
145
+ return [a.coefficient, b.coefficient * 10n ** BigInt(a.scale - b.scale)];
146
+ }
147
+ return [a.coefficient * 10n ** BigInt(b.scale - a.scale), b.coefficient];
148
+ }
@@ -0,0 +1,83 @@
1
+ import { Issue, Issues, type Result } from "./issue.ts";
2
+ import { Path } from "./path.ts";
3
+ /** How a decoder reads an input at a path. */
4
+ export type Run<T> = (input: unknown, path: Path) => Result<T>;
5
+ /**
6
+ * Reads a value of the input model into a `T`, or says everything that kept it from being one.
7
+ *
8
+ * What a decoder gives depends on the input alone, so a decoder can be kept and shared. An absent
9
+ * value is `undefined`: a missing member of an object is handed to its decoder as `undefined`,
10
+ * and decoders tell it from `null` where they say so.
11
+ */
12
+ export declare abstract class Decoder<T> {
13
+ /** Reads `input`, which is at `path` in what is being decoded; every issue is at a path below it. */
14
+ abstract decodeAt(input: unknown, path: Path): Result<T>;
15
+ /**
16
+ * A value this decoder gives, as an issue's metadata holds it. A float decoder's value is held
17
+ * with its width, so that the message writes it as a float of that width; every other value is
18
+ * held as it is.
19
+ */
20
+ metaValue(value: T): unknown;
21
+ /** Reads `input` as the whole of what is being decoded. */
22
+ decode(input: unknown): Result<T>;
23
+ /**
24
+ * Reads a JSON text: its numbers as written, its members in order. Text that is not JSON gives
25
+ * `invalid_format` under the message key `invalid_format.json`, at the root.
26
+ */
27
+ decodeJson(text: string): Result<T>;
28
+ /** A decoder giving what `f` makes of this one's value. */
29
+ map<U>(f: (value: T) => U): Decoder<U>;
30
+ /**
31
+ * A decoder giving what `f` makes of this one's value, which may be a failure: a rule that
32
+ * relates the parts of a value, checked once they exist. The paths of the issues `f` gives are
33
+ * read as relative to where this decoder is.
34
+ */
35
+ flatMap<U>(f: (value: T) => Result<U>): Decoder<U>;
36
+ /**
37
+ * This decoder, failing with `issue` where `predicate` does not hold of its value. The issue is
38
+ * given at this decoder's path, or below it where it has a path of its own.
39
+ */
40
+ refine(predicate: (value: T) => boolean, issue: Issue | ((value: T) => Issue)): Decoder<T>;
41
+ /** A decoder giving `null` for a JSON null, and reading anything else, absence included, with this one. */
42
+ nullable(): Decoder<T | null>;
43
+ /** A decoder giving `fallback` for a JSON null or an absent value, and reading anything else with this one. */
44
+ withDefault(fallback: T): Decoder<T>;
45
+ /** This decoder, giving `fallback` instead of any failure. */
46
+ recover(fallback: T): Decoder<T>;
47
+ /** This decoder, giving what `f` makes of the issues instead of any failure. */
48
+ recoverWith(f: (issues: Issues) => T): Decoder<T>;
49
+ }
50
+ /** A decoder that reads as `run` does. */
51
+ export declare function decoder<T>(run: Run<T>): Decoder<T>;
52
+ /**
53
+ * A decoder made of how it reads, to which checks are added one after another: each runs only
54
+ * where everything before it succeeded, and the decoder it makes is of the same class, so that
55
+ * the checks of that class can follow it.
56
+ */
57
+ export declare class Chain<T> extends Decoder<T> {
58
+ #private;
59
+ constructor(run: Run<T>);
60
+ decodeAt(input: unknown, path: Path): Result<T>;
61
+ /** A decoder of this class that reads as `run` does. */
62
+ protected derive(run: Run<T>): this;
63
+ /**
64
+ * This decoder, then `test` of its value: the issue it gives, at this decoder's path, with
65
+ * `message` as its sentence where one is given.
66
+ */
67
+ protected check(test: (value: T) => Issue | undefined, message?: string): this;
68
+ /** How this decoder reads, then `test` of its value. */
69
+ protected then(test: (value: T) => Issue | undefined, message?: string): Run<T>;
70
+ /**
71
+ * How this decoder reads, then what `f` makes of its value: the issues it gives at this
72
+ * decoder's path, each with `message` as its sentence where one is given.
73
+ */
74
+ protected convert<U>(f: (value: T) => Result<U>, message?: string): Run<U>;
75
+ }
76
+ /** A decoder giving `null` for a JSON null, and reading anything else, absence included, with `inner`. */
77
+ export declare function nullable<T>(inner: Decoder<T>): Decoder<T | null>;
78
+ /** A decoder giving `fallback` for a JSON null or an absent value, and reading anything else with `inner`. */
79
+ export declare function withDefault<T>(inner: Decoder<T>, fallback: T): Decoder<T>;
80
+ /** `inner`, giving `fallback` instead of any failure. */
81
+ export declare function recover<T>(inner: Decoder<T>, fallback: T): Decoder<T>;
82
+ /** `inner`, giving what `f` makes of the issues instead of any failure. */
83
+ export declare function recoverWith<T>(inner: Decoder<T>, f: (issues: Issues) => T): Decoder<T>;
@@ -0,0 +1,178 @@
1
+ // A value that describes how to read an input, and the ways decoders compose.
2
+ import { parse } from "./input.js";
3
+ import { Issue, Issues, failed, ok } from "./issue.js";
4
+ import { INVALID_FORMAT_JSON } from "./messages.js";
5
+ import { Path } from "./path.js";
6
+ /**
7
+ * Reads a value of the input model into a `T`, or says everything that kept it from being one.
8
+ *
9
+ * What a decoder gives depends on the input alone, so a decoder can be kept and shared. An absent
10
+ * value is `undefined`: a missing member of an object is handed to its decoder as `undefined`,
11
+ * and decoders tell it from `null` where they say so.
12
+ */
13
+ export class Decoder {
14
+ /**
15
+ * A value this decoder gives, as an issue's metadata holds it. A float decoder's value is held
16
+ * with its width, so that the message writes it as a float of that width; every other value is
17
+ * held as it is.
18
+ */
19
+ metaValue(value) {
20
+ return value;
21
+ }
22
+ /** Reads `input` as the whole of what is being decoded. */
23
+ decode(input) {
24
+ return this.decodeAt(input, Path.ROOT);
25
+ }
26
+ /**
27
+ * Reads a JSON text: its numbers as written, its members in order. Text that is not JSON gives
28
+ * `invalid_format` under the message key `invalid_format.json`, at the root.
29
+ */
30
+ decodeJson(text) {
31
+ let input;
32
+ try {
33
+ input = parse(text);
34
+ }
35
+ catch (e) {
36
+ if (e instanceof SyntaxError) {
37
+ return failed(new Issue("invalid_format", { messageKey: INVALID_FORMAT_JSON }));
38
+ }
39
+ throw e;
40
+ }
41
+ return this.decode(input);
42
+ }
43
+ /** A decoder giving what `f` makes of this one's value. */
44
+ map(f) {
45
+ return decoder((input, path) => {
46
+ const read = this.decodeAt(input, path);
47
+ return read.issues === undefined ? ok(f(read.value)) : read;
48
+ });
49
+ }
50
+ /**
51
+ * A decoder giving what `f` makes of this one's value, which may be a failure: a rule that
52
+ * relates the parts of a value, checked once they exist. The paths of the issues `f` gives are
53
+ * read as relative to where this decoder is.
54
+ */
55
+ flatMap(f) {
56
+ return decoder((input, path) => {
57
+ const read = this.decodeAt(input, path);
58
+ if (read.issues !== undefined) {
59
+ return read;
60
+ }
61
+ const made = f(read.value);
62
+ return made.issues === undefined ? made : failed(made.issues.under(path));
63
+ });
64
+ }
65
+ /**
66
+ * This decoder, failing with `issue` where `predicate` does not hold of its value. The issue is
67
+ * given at this decoder's path, or below it where it has a path of its own.
68
+ */
69
+ refine(predicate, issue) {
70
+ return this.flatMap((value) => (predicate(value) ? ok(value) : failed(typeof issue === "function" ? issue(value) : issue)));
71
+ }
72
+ /** A decoder giving `null` for a JSON null, and reading anything else, absence included, with this one. */
73
+ nullable() {
74
+ return nullable(this);
75
+ }
76
+ /** A decoder giving `fallback` for a JSON null or an absent value, and reading anything else with this one. */
77
+ withDefault(fallback) {
78
+ return withDefault(this, fallback);
79
+ }
80
+ /** This decoder, giving `fallback` instead of any failure. */
81
+ recover(fallback) {
82
+ return recover(this, fallback);
83
+ }
84
+ /** This decoder, giving what `f` makes of the issues instead of any failure. */
85
+ recoverWith(f) {
86
+ return recoverWith(this, f);
87
+ }
88
+ }
89
+ /** A decoder that reads as `run` does. */
90
+ export function decoder(run) {
91
+ return new Chain(run);
92
+ }
93
+ /**
94
+ * A decoder made of how it reads, to which checks are added one after another: each runs only
95
+ * where everything before it succeeded, and the decoder it makes is of the same class, so that
96
+ * the checks of that class can follow it.
97
+ */
98
+ export class Chain extends Decoder {
99
+ #run;
100
+ constructor(run) {
101
+ super();
102
+ this.#run = run;
103
+ }
104
+ decodeAt(input, path) {
105
+ return this.#run(input, path);
106
+ }
107
+ /** A decoder of this class that reads as `run` does. */
108
+ derive(run) {
109
+ return new this.constructor(run);
110
+ }
111
+ /**
112
+ * This decoder, then `test` of its value: the issue it gives, at this decoder's path, with
113
+ * `message` as its sentence where one is given.
114
+ */
115
+ check(test, message) {
116
+ return this.derive(this.then(test, message));
117
+ }
118
+ /** How this decoder reads, then `test` of its value. */
119
+ then(test, message) {
120
+ return this.convert((value) => {
121
+ const issue = test(value);
122
+ return issue === undefined ? ok(value) : failed(issue);
123
+ }, message);
124
+ }
125
+ /**
126
+ * How this decoder reads, then what `f` makes of its value: the issues it gives at this
127
+ * decoder's path, each with `message` as its sentence where one is given.
128
+ */
129
+ convert(f, message) {
130
+ return (input, path) => {
131
+ const read = this.decodeAt(input, path);
132
+ if (read.issues !== undefined) {
133
+ return read;
134
+ }
135
+ const made = f(read.value);
136
+ if (made.issues === undefined) {
137
+ return made;
138
+ }
139
+ const issues = message === undefined ? made.issues.list : made.issues.list.map((issue) => issue.withMessage(message));
140
+ return failed(new Issues(issues).under(path));
141
+ };
142
+ }
143
+ }
144
+ /** A decoder that reads as `run` does and gives values of `inner`'s kind, which it holds as metadata as `inner` does. */
145
+ class Around extends Decoder {
146
+ #run;
147
+ #inner;
148
+ constructor(inner, run) {
149
+ super();
150
+ this.#inner = inner;
151
+ this.#run = run;
152
+ }
153
+ decodeAt(input, path) {
154
+ return this.#run(input, path);
155
+ }
156
+ metaValue(value) {
157
+ return value === null || value === undefined ? value : this.#inner.metaValue(value);
158
+ }
159
+ }
160
+ /** A decoder giving `null` for a JSON null, and reading anything else, absence included, with `inner`. */
161
+ export function nullable(inner) {
162
+ return new Around(inner, (input, path) => (input === null ? ok(null) : inner.decodeAt(input, path)));
163
+ }
164
+ /** A decoder giving `fallback` for a JSON null or an absent value, and reading anything else with `inner`. */
165
+ export function withDefault(inner, fallback) {
166
+ return new Around(inner, (input, path) => input === null || input === undefined ? ok(fallback) : inner.decodeAt(input, path));
167
+ }
168
+ /** `inner`, giving `fallback` instead of any failure. */
169
+ export function recover(inner, fallback) {
170
+ return recoverWith(inner, () => fallback);
171
+ }
172
+ /** `inner`, giving what `f` makes of the issues instead of any failure. */
173
+ export function recoverWith(inner, f) {
174
+ return new Around(inner, (input, path) => {
175
+ const read = inner.decodeAt(input, path);
176
+ return read.issues === undefined ? read : ok(f(read.issues));
177
+ });
178
+ }
@@ -0,0 +1,28 @@
1
+ /** A value JSON can carry. */
2
+ export type Json = null | boolean | number | string | readonly Json[] | {
3
+ readonly [member: string]: Json;
4
+ };
5
+ /** Writes a `T` as JSON. */
6
+ export interface Encoder<T> {
7
+ encode(value: T): Json;
8
+ }
9
+ /** An encoder writing a string as a JSON string. */
10
+ export declare function string(): Encoder<string>;
11
+ /** One member of an object encoder: its name, and what it writes for a value. */
12
+ export interface Property<T> {
13
+ readonly name: string;
14
+ write(value: T): Json;
15
+ }
16
+ /** A member `name` holding what `encoder` writes of what `getter` reads of the value. */
17
+ export declare function property<T, P>(name: string, getter: (value: T) => P, encoder: Encoder<P>): Property<T>;
18
+ /**
19
+ * A member `name` holding what `encoder` writes of what `getter` reads of the value, or of
20
+ * `fallback` where it reads null or undefined.
21
+ */
22
+ export declare function propertyWithDefault<T, P>(name: string, getter: (value: T) => P | null | undefined, encoder: Encoder<P>, fallback: P): Property<T>;
23
+ /**
24
+ * An encoder writing a JSON object with one member per property, in the order declared.
25
+ *
26
+ * @throws {RangeError} where two properties write the same member
27
+ */
28
+ export declare function object<T>(...properties: readonly Property<T>[]): Encoder<T>;
package/dist/encode.js ADDED
@@ -0,0 +1,45 @@
1
+ // Encoders: writing a value as JSON.
2
+ /** An encoder writing a string as a JSON string. */
3
+ export function string() {
4
+ return { encode: (value) => value };
5
+ }
6
+ /** A member `name` holding what `encoder` writes of what `getter` reads of the value. */
7
+ export function property(name, getter, encoder) {
8
+ return { name, write: (value) => encoder.encode(getter(value)) };
9
+ }
10
+ /**
11
+ * A member `name` holding what `encoder` writes of what `getter` reads of the value, or of
12
+ * `fallback` where it reads null or undefined.
13
+ */
14
+ export function propertyWithDefault(name, getter, encoder, fallback) {
15
+ return {
16
+ name,
17
+ write: (value) => {
18
+ const read = getter(value);
19
+ return encoder.encode(read === null || read === undefined ? fallback : read);
20
+ },
21
+ };
22
+ }
23
+ /**
24
+ * An encoder writing a JSON object with one member per property, in the order declared.
25
+ *
26
+ * @throws {RangeError} where two properties write the same member
27
+ */
28
+ export function object(...properties) {
29
+ const names = new Set();
30
+ for (const { name } of properties) {
31
+ if (names.has(name)) {
32
+ throw new RangeError(`two properties write the member ${JSON.stringify(name)}`);
33
+ }
34
+ names.add(name);
35
+ }
36
+ return {
37
+ encode: (value) => {
38
+ const out = {};
39
+ for (const p of properties) {
40
+ Object.defineProperty(out, p.name, { value: p.write(value), enumerable: true, writable: true, configurable: true });
41
+ }
42
+ return out;
43
+ },
44
+ };
45
+ }
@@ -0,0 +1,58 @@
1
+ import { JsonNumber } from "./input.ts";
2
+ /** The width of a float: binary32 or binary64. */
3
+ export type Width = 32 | 64;
4
+ /**
5
+ * A float, kept with its width, as an issue's metadata holds one: the width decides how it is
6
+ * written in a message, so that a float32 bound of 0.1 reads `0.1` and not the binary64 digits of
7
+ * the same value.
8
+ */
9
+ export declare class Float {
10
+ readonly value: number;
11
+ readonly width: Width;
12
+ constructor(value: number, width: Width);
13
+ get [Symbol.toStringTag](): string;
14
+ valueOf(): number;
15
+ /** The message form: the canonical decimal, written as Java writes a float. */
16
+ toString(): string;
17
+ /** The observation of the float: its canonical decimal, or a tag where JSON cannot carry it. */
18
+ toJSON(): JsonNumber | {
19
+ float: string;
20
+ };
21
+ }
22
+ /**
23
+ * A float as the Raoh Specification observes it: its canonical decimal as a JSON number, or a
24
+ * tag, `{"float": "-0"}`, `{"float": "NaN"}`, `{"float": "+Infinity"}` or
25
+ * `{"float": "-Infinity"}`, where JSON cannot carry it.
26
+ */
27
+ export declare function floatJson(value: number, width: Width): JsonNumber | {
28
+ float: string;
29
+ };
30
+ /**
31
+ * The float of the given width nearest to ±coefficient × 10^exponent, rounding to nearest with
32
+ * ties to even, once: `Infinity` (with the sign) where it rounds beyond the largest finite value.
33
+ * The number is read exactly, so a float32 is not rounded through a binary64 first.
34
+ */
35
+ export declare function nearestFloat(negative: boolean, coefficient: bigint, exponent: number, width: Width): number;
36
+ /**
37
+ * The canonical decimal of a finite, non-zero float as `digits × 10^exponent`, with no trailing
38
+ * zero in `digits`: the decimal of the least length that rounds to the float, the one closest to
39
+ * it, ties going to the even coefficient; where one digit is enough, the closest decimal of one or
40
+ * two digits, so that the least float64 is `4.9E-324` rather than `5E-324`.
41
+ */
42
+ export declare function canonicalDecimal(value: number, width: Width): {
43
+ digits: string;
44
+ exponent: number;
45
+ };
46
+ /**
47
+ * The message form of a float: its canonical decimal, plain with at least one digit after the
48
+ * point where the first digit's exponent is from -3 to 6, and otherwise one digit, a point, the
49
+ * others (at least one), `E` and the exponent; `0.0`, `-0.0`, `NaN`, `Infinity`, `-Infinity`.
50
+ */
51
+ export declare function floatMessageForm(value: number, width: Width): string;
52
+ /**
53
+ * -1, 0 or 1 as `a` comes before, with or after `b` in the float order of the value model: -∞,
54
+ * the negative values, -0, +0, the positive values, +∞, and last NaN.
55
+ */
56
+ export declare function compareFloats(a: number, b: number): number;
57
+ /** Whether the two are the same float: +0 and -0 differ, and NaN is NaN. */
58
+ export declare function sameFloat(a: number, b: number): boolean;