@helpers4/number 2.0.1 → 2.0.3

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/README.md CHANGED
@@ -13,9 +13,15 @@ A set of helpers for working with URLs.
13
13
 
14
14
  <!-- AUTOMATIC-METHODS -->
15
15
  - clamp
16
+ - correctFloat
17
+ - extractNumber
16
18
  - formatCompact
17
19
  - formatSize
18
20
  - inRange
21
+ - isEven
22
+ - isNegative
23
+ - isOdd
24
+ - isPositive
19
25
  - lerp
20
26
  - mean
21
27
  - random
package/lib/index.d.ts CHANGED
@@ -13,6 +13,85 @@
13
13
  */
14
14
  declare function clamp(value: number, min: number, max: number): number;
15
15
 
16
+ /**
17
+ * This file is part of helpers4.
18
+ * Copyright (C) 2025 baxyz
19
+ * SPDX-License-Identifier: LGPL-3.0-or-later
20
+ */
21
+ /**
22
+ * Corrects floating-point arithmetic errors by rounding to a given number
23
+ * of significant digits. Useful after calculations that accumulate binary
24
+ * floating-point drift (e.g. `0.1 + 0.2 === 0.30000000000000004`).
25
+ *
26
+ * The default precision of 14 significant digits eliminates typical
27
+ * rounding noise for values in the range used by most applications.
28
+ * Note: for values whose integer part already consumes 14 or more digits
29
+ * (i.e. |value| ≥ 1e13), toPrecision(14) has no room left for decimal
30
+ * digits and will silently truncate them. Increase `precision` if you
31
+ * need to correct drift in very large numbers.
32
+ *
33
+ * @param value - The floating-point value to correct
34
+ * @param precision - Integer number of significant digits (default: 14)
35
+ * @returns The corrected value
36
+ * @since 2.0.2
37
+ */
38
+ declare function correctFloat(value: number, precision?: number): number;
39
+
40
+ /**
41
+ * This file is part of helpers4.
42
+ * Copyright (C) 2025 baxyz
43
+ * SPDX-License-Identifier: LGPL-3.0-or-later
44
+ */
45
+ /**
46
+ * Options for {@link extractNumber}.
47
+ */
48
+ interface ExtractNumberOptions {
49
+ /**
50
+ * How to interpret a `-` immediately before the matched digits.
51
+ * - `'auto'`: treated as a minus sign unless it is glued to a preceding letter/digit
52
+ * (e.g. `'-111'` and `'xxx -111'` → negative, but `'xxx-111'` → separator, positive).
53
+ * - `'strict'`: always treated as a minus sign.
54
+ * - `'ignore'`: never treated as a minus sign (always a separator).
55
+ * @default 'auto'
56
+ */
57
+ sign?: 'auto' | 'strict' | 'ignore';
58
+ /**
59
+ * How to interpret a trailing scientific-notation suffix (`e`/`E` + digits, e.g. `1.5e-10`).
60
+ * - `'auto'`: treated as an exponent unless it is glued to a following letter/digit
61
+ * (e.g. `'1e5'` and `'1e5 mol'` → exponential, but `'1e5kg'` → mantissa only).
62
+ * - `'strict'`: always treated as an exponent.
63
+ * - `'ignore'`: never treated as an exponent (mantissa only).
64
+ * @default 'auto'
65
+ */
66
+ exponent?: 'auto' | 'strict' | 'ignore';
67
+ }
68
+ /**
69
+ * Extracts the first number embedded anywhere in a string, or passes through a `number`.
70
+ *
71
+ * Unlike a plain `parseFloat`/`parseInt`, the number does not need to be at the start of
72
+ * the string: digits are searched for anywhere, so leading/trailing text (units, labels, ...)
73
+ * is ignored. A `-` before the digits and a scientific-notation suffix (`e`/`E`) are
74
+ * disambiguated with {@link ExtractNumberOptions.sign} and {@link ExtractNumberOptions.exponent}.
75
+ *
76
+ * Returns `undefined` if no number can be found.
77
+ *
78
+ * @param value - The value to extract a number from
79
+ * @param options - Options controlling sign and exponent disambiguation
80
+ * @returns The extracted number, or `undefined` if none was found
81
+ * @example
82
+ * extractNumber('16.5px') // => 16.5
83
+ * extractNumber('Wafer 10') // => 10
84
+ * extractNumber('xxx-111') // => 111 ('-' glued to text → separator)
85
+ * extractNumber('xxx -111') // => -111 ('-' preceded by a space → sign)
86
+ * extractNumber('-111') // => -111 ('-' at the start of the string → sign)
87
+ * extractNumber('1e5 mol') // => 100000
88
+ * extractNumber('1e5kg') // => 1 ('e5' glued to text → mantissa only)
89
+ * extractNumber('no number') // => undefined
90
+ * extractNumber(42) // => 42
91
+ * @since 2.0.3
92
+ */
93
+ declare function extractNumber(value: unknown, options?: ExtractNumberOptions): number | undefined;
94
+
16
95
  /**
17
96
  * This file is part of helpers4.
18
97
  * Copyright (C) 2025 baxyz
@@ -95,6 +174,98 @@ interface InRangeOptions {
95
174
  */
96
175
  declare function inRange(value: number, min: number, max: number, options?: InRangeOptions): boolean;
97
176
 
177
+ /**
178
+ * This file is part of helpers4.
179
+ * Copyright (C) 2025 baxyz
180
+ * SPDX-License-Identifier: LGPL-3.0-or-later
181
+ */
182
+ /**
183
+ * Checks if a value is an even integer.
184
+ *
185
+ * Returns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and odd integers.
186
+ *
187
+ * @param value - The value to check
188
+ * @returns `true` if value is an integer divisible by 2
189
+ * @example
190
+ * isEven(2) // => true
191
+ * isEven(0) // => true
192
+ * isEven(-4) // => true
193
+ * isEven(3) // => false
194
+ * isEven(1.5) // => false (not an integer)
195
+ * isEven('2') // => false
196
+ * @since 2.0.3
197
+ */
198
+ declare function isEven(value: unknown): value is number;
199
+
200
+ /**
201
+ * This file is part of helpers4.
202
+ * Copyright (C) 2025 baxyz
203
+ * SPDX-License-Identifier: LGPL-3.0-or-later
204
+ */
205
+ /**
206
+ * Checks if a value is a number less than 0.
207
+ *
208
+ * Returns `false` for `NaN`, `0`, positive numbers, and non-number types.
209
+ *
210
+ * @param value - The value to check
211
+ * @returns True if value is a negative number
212
+ * @example
213
+ * isNegative(-1) // => true
214
+ * isNegative(-0.5) // => true
215
+ * isNegative(-Infinity) // => true
216
+ * isNegative(0) // => false
217
+ * isNegative(1) // => false
218
+ * isNegative(NaN) // => false
219
+ * @since 2.0.0
220
+ */
221
+ declare function isNegative(value: unknown): value is number;
222
+
223
+ /**
224
+ * This file is part of helpers4.
225
+ * Copyright (C) 2025 baxyz
226
+ * SPDX-License-Identifier: LGPL-3.0-or-later
227
+ */
228
+ /**
229
+ * Checks if a value is an odd integer.
230
+ *
231
+ * Returns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and even integers.
232
+ *
233
+ * @param value - The value to check
234
+ * @returns `true` if value is an integer not divisible by 2
235
+ * @example
236
+ * isOdd(3) // => true
237
+ * isOdd(1) // => true
238
+ * isOdd(-7) // => true
239
+ * isOdd(2) // => false
240
+ * isOdd(1.5) // => false (not an integer)
241
+ * isOdd('3') // => false
242
+ * @since 2.0.3
243
+ */
244
+ declare function isOdd(value: unknown): value is number;
245
+
246
+ /**
247
+ * This file is part of helpers4.
248
+ * Copyright (C) 2025 baxyz
249
+ * SPDX-License-Identifier: LGPL-3.0-or-later
250
+ */
251
+ /**
252
+ * Checks if a value is a number greater than 0.
253
+ *
254
+ * Returns `false` for `NaN`, `0`, negative numbers, and non-number types.
255
+ *
256
+ * @param value - The value to check
257
+ * @returns True if value is a positive number
258
+ * @example
259
+ * isPositive(42) // => true
260
+ * isPositive(0.1) // => true
261
+ * isPositive(Infinity) // => true
262
+ * isPositive(0) // => false
263
+ * isPositive(-1) // => false
264
+ * isPositive(NaN) // => false
265
+ * @since 2.0.0
266
+ */
267
+ declare function isPositive(value: unknown): value is number;
268
+
98
269
  /**
99
270
  * This file is part of helpers4.
100
271
  * Copyright (C) 2025 baxyz
@@ -193,5 +364,5 @@ declare function roundTo(value: number, decimals: number): number;
193
364
  */
194
365
  declare function sum(array: readonly number[]): number;
195
366
 
196
- export { clamp, formatCompact, formatSize, inRange, lerp, mean, randomBetween, randomIntBetween, roundTo, sum };
197
- export type { InRangeOptions };
367
+ export { clamp, correctFloat, extractNumber, formatCompact, formatSize, inRange, isEven, isNegative, isOdd, isPositive, lerp, mean, randomBetween, randomIntBetween, roundTo, sum };
368
+ export type { ExtractNumberOptions, InRangeOptions };
package/lib/index.js CHANGED
@@ -16,6 +16,88 @@ function clamp(value, min, max) {
16
16
  return Math.min(Math.max(value, min), max);
17
17
  }
18
18
  //#endregion
19
+ //#region helpers/number/correctFloat.ts
20
+ /**
21
+ * This file is part of helpers4.
22
+ * Copyright (C) 2025 baxyz
23
+ * SPDX-License-Identifier: LGPL-3.0-or-later
24
+ */
25
+ /**
26
+ * Corrects floating-point arithmetic errors by rounding to a given number
27
+ * of significant digits. Useful after calculations that accumulate binary
28
+ * floating-point drift (e.g. `0.1 + 0.2 === 0.30000000000000004`).
29
+ *
30
+ * The default precision of 14 significant digits eliminates typical
31
+ * rounding noise for values in the range used by most applications.
32
+ * Note: for values whose integer part already consumes 14 or more digits
33
+ * (i.e. |value| ≥ 1e13), toPrecision(14) has no room left for decimal
34
+ * digits and will silently truncate them. Increase `precision` if you
35
+ * need to correct drift in very large numbers.
36
+ *
37
+ * @param value - The floating-point value to correct
38
+ * @param precision - Integer number of significant digits (default: 14)
39
+ * @returns The corrected value
40
+ * @since 2.0.2
41
+ */
42
+ function correctFloat(value, precision = 14) {
43
+ if (!Number.isFinite(value)) throw new RangeError(`correctFloat: value must be a finite number, got ${value}`);
44
+ if (!Number.isInteger(precision) || precision < 1 || precision > 100) throw new RangeError(`correctFloat: precision must be an integer between 1 and 100, got ${precision}`);
45
+ const result = parseFloat(value.toPrecision(precision));
46
+ return Object.is(value, -0) && result === 0 ? -0 : result;
47
+ }
48
+ //#endregion
49
+ //#region helpers/number/extractNumber.ts
50
+ var NUMBER_TOKEN = /(-)?(\d+(?:\.\d+)?)([eE][+-]?\d+)?/g;
51
+ function isWordChar(char) {
52
+ return char !== void 0 && /[\p{L}\p{N}_]/u.test(char);
53
+ }
54
+ /**
55
+ * Extracts the first number embedded anywhere in a string, or passes through a `number`.
56
+ *
57
+ * Unlike a plain `parseFloat`/`parseInt`, the number does not need to be at the start of
58
+ * the string: digits are searched for anywhere, so leading/trailing text (units, labels, ...)
59
+ * is ignored. A `-` before the digits and a scientific-notation suffix (`e`/`E`) are
60
+ * disambiguated with {@link ExtractNumberOptions.sign} and {@link ExtractNumberOptions.exponent}.
61
+ *
62
+ * Returns `undefined` if no number can be found.
63
+ *
64
+ * @param value - The value to extract a number from
65
+ * @param options - Options controlling sign and exponent disambiguation
66
+ * @returns The extracted number, or `undefined` if none was found
67
+ * @example
68
+ * extractNumber('16.5px') // => 16.5
69
+ * extractNumber('Wafer 10') // => 10
70
+ * extractNumber('xxx-111') // => 111 ('-' glued to text → separator)
71
+ * extractNumber('xxx -111') // => -111 ('-' preceded by a space → sign)
72
+ * extractNumber('-111') // => -111 ('-' at the start of the string → sign)
73
+ * extractNumber('1e5 mol') // => 100000
74
+ * extractNumber('1e5kg') // => 1 ('e5' glued to text → mantissa only)
75
+ * extractNumber('no number') // => undefined
76
+ * extractNumber(42) // => 42
77
+ * @since 2.0.3
78
+ */
79
+ function extractNumber(value, options = {}) {
80
+ const { sign = "auto", exponent = "auto" } = options;
81
+ if (typeof value === "number") return Number.isNaN(value) ? void 0 : value;
82
+ if (typeof value !== "string") return;
83
+ NUMBER_TOKEN.lastIndex = 0;
84
+ let match;
85
+ while ((match = NUMBER_TOKEN.exec(value)) !== null) {
86
+ const [full, signChar, mantissa, exponentPart] = match;
87
+ const start = match.index;
88
+ if (signChar) {
89
+ const before = value[start - 1];
90
+ if (!(sign === "strict" ? true : sign === "ignore" ? false : !isWordChar(before))) {
91
+ NUMBER_TOKEN.lastIndex = start + 1;
92
+ continue;
93
+ }
94
+ }
95
+ const keepExponent = exponentPart ? exponent === "strict" ? true : exponent === "ignore" ? false : !isWordChar(value[start + full.length]) : false;
96
+ const numericText = (signChar ?? "") + mantissa + (keepExponent ? exponentPart : "");
97
+ return Number(numericText);
98
+ }
99
+ }
100
+ //#endregion
19
101
  //#region helpers/number/formatCompact.ts
20
102
  /**
21
103
  * This file is part of helpers4.
@@ -104,6 +186,110 @@ function inRange(value, min, max, options = {}) {
104
186
  return (inclusive === "both" || inclusive === "min" ? value >= min : value > min) && (inclusive === "both" || inclusive === "max" ? value <= max : value < max);
105
187
  }
106
188
  //#endregion
189
+ //#region helpers/number/isEven.ts
190
+ /**
191
+ * This file is part of helpers4.
192
+ * Copyright (C) 2025 baxyz
193
+ * SPDX-License-Identifier: LGPL-3.0-or-later
194
+ */
195
+ /**
196
+ * Checks if a value is an even integer.
197
+ *
198
+ * Returns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and odd integers.
199
+ *
200
+ * @param value - The value to check
201
+ * @returns `true` if value is an integer divisible by 2
202
+ * @example
203
+ * isEven(2) // => true
204
+ * isEven(0) // => true
205
+ * isEven(-4) // => true
206
+ * isEven(3) // => false
207
+ * isEven(1.5) // => false (not an integer)
208
+ * isEven('2') // => false
209
+ * @since 2.0.3
210
+ */
211
+ function isEven(value) {
212
+ return typeof value === "number" && Number.isInteger(value) && value % 2 === 0;
213
+ }
214
+ //#endregion
215
+ //#region helpers/number/isNegative.ts
216
+ /**
217
+ * This file is part of helpers4.
218
+ * Copyright (C) 2025 baxyz
219
+ * SPDX-License-Identifier: LGPL-3.0-or-later
220
+ */
221
+ /**
222
+ * Checks if a value is a number less than 0.
223
+ *
224
+ * Returns `false` for `NaN`, `0`, positive numbers, and non-number types.
225
+ *
226
+ * @param value - The value to check
227
+ * @returns True if value is a negative number
228
+ * @example
229
+ * isNegative(-1) // => true
230
+ * isNegative(-0.5) // => true
231
+ * isNegative(-Infinity) // => true
232
+ * isNegative(0) // => false
233
+ * isNegative(1) // => false
234
+ * isNegative(NaN) // => false
235
+ * @since 2.0.0
236
+ */
237
+ function isNegative(value) {
238
+ return typeof value === "number" && value < 0;
239
+ }
240
+ //#endregion
241
+ //#region helpers/number/isOdd.ts
242
+ /**
243
+ * This file is part of helpers4.
244
+ * Copyright (C) 2025 baxyz
245
+ * SPDX-License-Identifier: LGPL-3.0-or-later
246
+ */
247
+ /**
248
+ * Checks if a value is an odd integer.
249
+ *
250
+ * Returns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and even integers.
251
+ *
252
+ * @param value - The value to check
253
+ * @returns `true` if value is an integer not divisible by 2
254
+ * @example
255
+ * isOdd(3) // => true
256
+ * isOdd(1) // => true
257
+ * isOdd(-7) // => true
258
+ * isOdd(2) // => false
259
+ * isOdd(1.5) // => false (not an integer)
260
+ * isOdd('3') // => false
261
+ * @since 2.0.3
262
+ */
263
+ function isOdd(value) {
264
+ return typeof value === "number" && Number.isInteger(value) && value % 2 !== 0;
265
+ }
266
+ //#endregion
267
+ //#region helpers/number/isPositive.ts
268
+ /**
269
+ * This file is part of helpers4.
270
+ * Copyright (C) 2025 baxyz
271
+ * SPDX-License-Identifier: LGPL-3.0-or-later
272
+ */
273
+ /**
274
+ * Checks if a value is a number greater than 0.
275
+ *
276
+ * Returns `false` for `NaN`, `0`, negative numbers, and non-number types.
277
+ *
278
+ * @param value - The value to check
279
+ * @returns True if value is a positive number
280
+ * @example
281
+ * isPositive(42) // => true
282
+ * isPositive(0.1) // => true
283
+ * isPositive(Infinity) // => true
284
+ * isPositive(0) // => false
285
+ * isPositive(-1) // => false
286
+ * isPositive(NaN) // => false
287
+ * @since 2.0.0
288
+ */
289
+ function isPositive(value) {
290
+ return typeof value === "number" && value > 0;
291
+ }
292
+ //#endregion
107
293
  //#region helpers/number/lerp.ts
108
294
  /**
109
295
  * This file is part of helpers4.
@@ -224,6 +410,6 @@ function sum(array) {
224
410
  return array.reduce((acc, n) => acc + n, 0);
225
411
  }
226
412
  //#endregion
227
- export { clamp, formatCompact, formatSize, inRange, lerp, mean, randomBetween, randomIntBetween, roundTo, sum };
413
+ export { clamp, correctFloat, extractNumber, formatCompact, formatSize, inRange, isEven, isNegative, isOdd, isPositive, lerp, mean, randomBetween, randomIntBetween, roundTo, sum };
228
414
 
229
415
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../helpers/number/clamp.ts","../../../helpers/number/formatCompact.ts","../../../helpers/number/formatSize.ts","../../../helpers/number/inRange.ts","../../../helpers/number/lerp.ts","../../../helpers/number/mean.ts","../../../helpers/number/random.ts","../../../helpers/number/roundTo.ts","../../../helpers/number/sum.ts"],"sourcesContent":["/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Clamps a number between min and max values\n * @param value - The value to clamp\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Clamped value\n * @since 1.9.0\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Formats a number using compact notation (e.g. `1_500_000 → \"1.5M\"`).\n *\n * Thin wrapper over `Intl.NumberFormat` with `notation: 'compact'`. Companion\n * of `formatSize` in the same `format*` family.\n *\n * @param value - The number to format.\n * @param locale - BCP 47 locale tag. Defaults to the runtime locale.\n * @returns A compact string representation of the number.\n * @example\n * formatCompact(1_500_000); // '1.5M' (en-US locale)\n * formatCompact(1_000); // '1K'\n * formatCompact(999); // '999'\n * formatCompact(1_500_000, 'fr'); // '1,5 M'\n * @since 2.0.0\n */\nexport function formatCompact(value: number, locale?: string): string {\n return new Intl.NumberFormat(locale, { notation: 'compact' }).format(value);\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/** Size units in ascending order. */\nconst UNITS = ['B', 'KB', 'MB', 'GB', 'TB'] as const;\n\n/**\n * Format a byte count into a human-readable string with the appropriate unit.\n *\n * Each unit is 1024 of the previous (binary prefix). The result is formatted\n * with one decimal place.\n *\n * @param bytes - A non-negative integer representing a byte count.\n * @returns A human-readable string such as `'0.0B'`, `'1.5KB'`, `'3.2MB'`.\n * @example\n * formatSize(0) // '0.0B'\n * formatSize(512) // '512.0B'\n * formatSize(1024) // '1.0KB'\n * formatSize(1536) // '1.5KB'\n * formatSize(1_048_576) // '1.0MB'\n * formatSize(1_073_741_824) // '1.0GB'\n * @since 2.0.0\n */\nexport function formatSize(bytes: number): string {\n let size = bytes;\n let unitIndex = 0;\n\n while (size >= 1024 && unitIndex < UNITS.length - 1) {\n size /= 1024;\n unitIndex++;\n }\n\n return `${size.toFixed(1)}${UNITS[unitIndex]}`;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Options for {@link inRange}.\n */\nexport interface InRangeOptions {\n /**\n * Which boundary (or boundaries) are included in the range.\n * - `'both'` (default): `min <= value <= max`\n * - `'min'`: `min <= value < max`\n * - `'max'`: `min < value <= max`\n * - `'none'`: `min < value < max`\n */\n inclusive?: 'both' | 'min' | 'max' | 'none';\n}\n\n/**\n * Checks whether a number falls within `[min, max]` (both inclusive by default).\n *\n * @param value - The number to test\n * @param min - Lower bound\n * @param max - Upper bound\n * @param options - Boundary inclusion mode (default: `'both'`)\n * @returns `true` if `value` is within the specified range\n * @example\n * inRange(5, 1, 10) // => true\n * inRange(1, 1, 10) // => true (min inclusive)\n * inRange(1, 1, 10, { inclusive: 'none' }) // => false\n * inRange(10, 1, 10, { inclusive: 'max' }) // => true\n * @since 2.0.0\n */\nexport function inRange(value: number, min: number, max: number, options: InRangeOptions = {}): boolean {\n const { inclusive = 'both' } = options;\n const minOk = inclusive === 'both' || inclusive === 'min' ? value >= min : value > min;\n const maxOk = inclusive === 'both' || inclusive === 'max' ? value <= max : value < max;\n return minOk && maxOk;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Linearly interpolates between `start` and `end` by the factor `t`.\n *\n * - `t = 0` returns `start`.\n * - `t = 1` returns `end`.\n * - Values of `t` outside `[0, 1]` extrapolate beyond the range.\n *\n * @param start - The start value.\n * @param end - The end value.\n * @param t - The interpolation factor.\n * @returns The interpolated value.\n * @example\n * lerp(0, 100, 0); // 0\n * lerp(0, 100, 1); // 100\n * lerp(0, 100, 0.5); // 50\n * lerp(0, 100, 0.25); // 25\n * @since 2.0.0\n */\nexport function lerp(start: number, end: number, t: number): number {\n if (t === 0) return start;\n if (t === 1) return end;\n if (Object.is(start, end)) return start;\n return start + (end - start) * t;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Calculates the arithmetic mean (average) of an array of numbers.\n * Returns `NaN` for an empty array.\n *\n * Pairs with {@link sum} for aggregate operations.\n *\n * @param array - The array of numbers to average\n * @returns The arithmetic mean, or `NaN` if the array is empty\n * @example\n * mean([1, 2, 3, 4]) // => 2.5\n * mean([10]) // => 10\n * mean([]) // => NaN\n * @since 2.0.0\n */\nexport function mean(array: readonly number[]): number {\n if (array.length === 0) return NaN;\n return array.reduce((acc, n) => acc + n, 0) / array.length;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Generates a random number between min and max (inclusive)\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Random number between min and max\n * @since 1.9.0\n */\nexport function randomBetween(min: number, max: number): number {\n return Math.random() * (max - min) + min;\n}\n\n/**\n * Generates a random integer between min and max (inclusive)\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Random integer between min and max\n * @since 1.9.0\n */\nexport function randomIntBetween(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Rounds a number to specified decimal places\n * @param value - The number to round\n * @param decimals - Number of decimal places\n * @returns Rounded number\n * @since 1.9.0\n */\nexport function roundTo(value: number, decimals: number): number {\n const multiplier = Math.pow(10, decimals);\n return Math.round(value * multiplier) / multiplier;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Calculates the sum of an array of numbers.\n * @param array - The array of numbers to sum\n * @returns The sum of all values\n * @example\n * sum([1, 2, 3, 4])\n * // => 10\n * @since 2.0.0\n */\nexport function sum(array: readonly number[]): number {\n return array.reduce((acc, n) => acc + n, 0);\n}\n"],"mappings":";;;;;;;;;;;;;;AAcA,SAAgB,MAAM,OAAe,KAAa,KAAqB;CACrE,OAAO,KAAK,IAAI,KAAK,IAAI,OAAO,GAAG,GAAG,GAAG;AAC3C;;;;;;;;;;;;;;;;;;;;;;;;ACMA,SAAgB,cAAc,OAAe,QAAyB;CACpE,OAAO,IAAI,KAAK,aAAa,QAAQ,EAAE,UAAU,UAAU,CAAC,EAAE,OAAO,KAAK;AAC5E;;;;;;;;;ACjBA,IAAM,QAAQ;CAAC;CAAK;CAAM;CAAM;CAAM;AAAI;;;;;;;;;;;;;;;;;;AAmB1C,SAAgB,WAAW,OAAuB;CAChD,IAAI,OAAO;CACX,IAAI,YAAY;CAEhB,OAAO,QAAQ,QAAQ,YAAY,MAAM,SAAS,GAAG;EACnD,QAAQ;EACR;CACF;CAEA,OAAO,GAAG,KAAK,QAAQ,CAAC,IAAI,MAAM;AACpC;;;;;;;;;;;;;;;;;;ACDA,SAAgB,QAAQ,OAAe,KAAa,KAAa,UAA0B,CAAC,GAAY;CACtG,MAAM,EAAE,YAAY,WAAW;CAG/B,QAFc,cAAc,UAAU,cAAc,QAAQ,SAAS,MAAM,QAAQ,SACrE,cAAc,UAAU,cAAc,QAAQ,SAAS,MAAM,QAAQ;AAErF;;;;;;;;;;;;;;;;;;;;;;;;;;AChBA,SAAgB,KAAK,OAAe,KAAa,GAAmB;CAClE,IAAI,MAAM,GAAG,OAAO;CACpB,IAAI,MAAM,GAAG,OAAO;CACpB,IAAI,OAAO,GAAG,OAAO,GAAG,GAAG,OAAO;CAClC,OAAO,SAAS,MAAM,SAAS;AACjC;;;;;;;;;;;;;;;;;;;;;;ACTA,SAAgB,KAAK,OAAkC;CACrD,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,OAAO,MAAM,QAAQ,KAAK,MAAM,MAAM,GAAG,CAAC,IAAI,MAAM;AACtD;;;;;;;;;;;;;;;ACVA,SAAgB,cAAc,KAAa,KAAqB;CAC9D,OAAO,KAAK,OAAO,KAAK,MAAM,OAAO;AACvC;;;;;;;;AASA,SAAgB,iBAAiB,KAAa,KAAqB;CACjE,OAAO,KAAK,MAAM,KAAK,OAAO,KAAK,MAAM,MAAM,EAAE,IAAI;AACvD;;;;;;;;;;;;;;;ACbA,SAAgB,QAAQ,OAAe,UAA0B;CAC/D,MAAM,aAAa,KAAK,IAAI,IAAI,QAAQ;CACxC,OAAO,KAAK,MAAM,QAAQ,UAAU,IAAI;AAC1C;;;;;;;;;;;;;;;;;ACDA,SAAgB,IAAI,OAAkC;CACpD,OAAO,MAAM,QAAQ,KAAK,MAAM,MAAM,GAAG,CAAC;AAC5C"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../helpers/number/clamp.ts","../../../helpers/number/correctFloat.ts","../../../helpers/number/extractNumber.ts","../../../helpers/number/formatCompact.ts","../../../helpers/number/formatSize.ts","../../../helpers/number/inRange.ts","../../../helpers/number/isEven.ts","../../../helpers/number/isNegative.ts","../../../helpers/number/isOdd.ts","../../../helpers/number/isPositive.ts","../../../helpers/number/lerp.ts","../../../helpers/number/mean.ts","../../../helpers/number/random.ts","../../../helpers/number/roundTo.ts","../../../helpers/number/sum.ts"],"sourcesContent":["/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Clamps a number between min and max values\n * @param value - The value to clamp\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Clamped value\n * @since 1.9.0\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Corrects floating-point arithmetic errors by rounding to a given number\n * of significant digits. Useful after calculations that accumulate binary\n * floating-point drift (e.g. `0.1 + 0.2 === 0.30000000000000004`).\n *\n * The default precision of 14 significant digits eliminates typical\n * rounding noise for values in the range used by most applications.\n * Note: for values whose integer part already consumes 14 or more digits\n * (i.e. |value| ≥ 1e13), toPrecision(14) has no room left for decimal\n * digits and will silently truncate them. Increase `precision` if you\n * need to correct drift in very large numbers.\n *\n * @param value - The floating-point value to correct\n * @param precision - Integer number of significant digits (default: 14)\n * @returns The corrected value\n * @since 2.0.2\n */\nexport function correctFloat(value: number, precision: number = 14): number {\n if (!Number.isFinite(value)) {\n throw new RangeError(`correctFloat: value must be a finite number, got ${value}`);\n }\n if (!Number.isInteger(precision) || precision < 1 || precision > 100) {\n throw new RangeError(`correctFloat: precision must be an integer between 1 and 100, got ${precision}`);\n }\n const result = parseFloat(value.toPrecision(precision));\n // toPrecision(-0) produces \"0.000…\" (no minus sign per spec) — restore the sign bit.\n return Object.is(value, -0) && result === 0 ? -0 : result;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Options for {@link extractNumber}.\n */\nexport interface ExtractNumberOptions {\n /**\n * How to interpret a `-` immediately before the matched digits.\n * - `'auto'`: treated as a minus sign unless it is glued to a preceding letter/digit\n * (e.g. `'-111'` and `'xxx -111'` → negative, but `'xxx-111'` → separator, positive).\n * - `'strict'`: always treated as a minus sign.\n * - `'ignore'`: never treated as a minus sign (always a separator).\n * @default 'auto'\n */\n sign?: 'auto' | 'strict' | 'ignore';\n\n /**\n * How to interpret a trailing scientific-notation suffix (`e`/`E` + digits, e.g. `1.5e-10`).\n * - `'auto'`: treated as an exponent unless it is glued to a following letter/digit\n * (e.g. `'1e5'` and `'1e5 mol'` → exponential, but `'1e5kg'` → mantissa only).\n * - `'strict'`: always treated as an exponent.\n * - `'ignore'`: never treated as an exponent (mantissa only).\n * @default 'auto'\n */\n exponent?: 'auto' | 'strict' | 'ignore';\n}\n\nconst NUMBER_TOKEN = /(-)?(\\d+(?:\\.\\d+)?)([eE][+-]?\\d+)?/g;\n\nfunction isWordChar(char: string | undefined): boolean {\n return char !== undefined && /[\\p{L}\\p{N}_]/u.test(char);\n}\n\n/**\n * Extracts the first number embedded anywhere in a string, or passes through a `number`.\n *\n * Unlike a plain `parseFloat`/`parseInt`, the number does not need to be at the start of\n * the string: digits are searched for anywhere, so leading/trailing text (units, labels, ...)\n * is ignored. A `-` before the digits and a scientific-notation suffix (`e`/`E`) are\n * disambiguated with {@link ExtractNumberOptions.sign} and {@link ExtractNumberOptions.exponent}.\n *\n * Returns `undefined` if no number can be found.\n *\n * @param value - The value to extract a number from\n * @param options - Options controlling sign and exponent disambiguation\n * @returns The extracted number, or `undefined` if none was found\n * @example\n * extractNumber('16.5px') // => 16.5\n * extractNumber('Wafer 10') // => 10\n * extractNumber('xxx-111') // => 111 ('-' glued to text → separator)\n * extractNumber('xxx -111') // => -111 ('-' preceded by a space → sign)\n * extractNumber('-111') // => -111 ('-' at the start of the string → sign)\n * extractNumber('1e5 mol') // => 100000\n * extractNumber('1e5kg') // => 1 ('e5' glued to text → mantissa only)\n * extractNumber('no number') // => undefined\n * extractNumber(42) // => 42\n * @since 2.0.3\n */\nexport function extractNumber(value: unknown, options: ExtractNumberOptions = {}): number | undefined {\n const { sign = 'auto', exponent = 'auto' } = options;\n\n if (typeof value === 'number') {\n return Number.isNaN(value) ? undefined : value;\n }\n\n if (typeof value !== 'string') {\n return undefined;\n }\n\n NUMBER_TOKEN.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = NUMBER_TOKEN.exec(value)) !== null) {\n const [full, signChar, mantissa, exponentPart] = match;\n const start = match.index;\n\n if (signChar) {\n const before = value[start - 1];\n const keepSign = sign === 'strict' ? true : sign === 'ignore' ? false : !isWordChar(before);\n\n if (!keepSign) {\n // Re-scan from right after the '-' so the digits are matched as a fresh, unsigned number.\n NUMBER_TOKEN.lastIndex = start + 1;\n continue;\n }\n }\n\n const keepExponent = exponentPart\n ? exponent === 'strict'\n ? true\n : exponent === 'ignore'\n ? false\n : !isWordChar(value[start + full.length])\n : false;\n\n const numericText = (signChar ?? '') + mantissa + (keepExponent ? exponentPart : '');\n return Number(numericText);\n }\n\n return undefined;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Formats a number using compact notation (e.g. `1_500_000 → \"1.5M\"`).\n *\n * Thin wrapper over `Intl.NumberFormat` with `notation: 'compact'`. Companion\n * of `formatSize` in the same `format*` family.\n *\n * @param value - The number to format.\n * @param locale - BCP 47 locale tag. Defaults to the runtime locale.\n * @returns A compact string representation of the number.\n * @example\n * formatCompact(1_500_000); // '1.5M' (en-US locale)\n * formatCompact(1_000); // '1K'\n * formatCompact(999); // '999'\n * formatCompact(1_500_000, 'fr'); // '1,5 M'\n * @since 2.0.0\n */\nexport function formatCompact(value: number, locale?: string): string {\n return new Intl.NumberFormat(locale, { notation: 'compact' }).format(value);\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/** Size units in ascending order. */\nconst UNITS = ['B', 'KB', 'MB', 'GB', 'TB'] as const;\n\n/**\n * Format a byte count into a human-readable string with the appropriate unit.\n *\n * Each unit is 1024 of the previous (binary prefix). The result is formatted\n * with one decimal place.\n *\n * @param bytes - A non-negative integer representing a byte count.\n * @returns A human-readable string such as `'0.0B'`, `'1.5KB'`, `'3.2MB'`.\n * @example\n * formatSize(0) // '0.0B'\n * formatSize(512) // '512.0B'\n * formatSize(1024) // '1.0KB'\n * formatSize(1536) // '1.5KB'\n * formatSize(1_048_576) // '1.0MB'\n * formatSize(1_073_741_824) // '1.0GB'\n * @since 2.0.0\n */\nexport function formatSize(bytes: number): string {\n let size = bytes;\n let unitIndex = 0;\n\n while (size >= 1024 && unitIndex < UNITS.length - 1) {\n size /= 1024;\n unitIndex++;\n }\n\n return `${size.toFixed(1)}${UNITS[unitIndex]}`;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Options for {@link inRange}.\n */\nexport interface InRangeOptions {\n /**\n * Which boundary (or boundaries) are included in the range.\n * - `'both'` (default): `min <= value <= max`\n * - `'min'`: `min <= value < max`\n * - `'max'`: `min < value <= max`\n * - `'none'`: `min < value < max`\n */\n inclusive?: 'both' | 'min' | 'max' | 'none';\n}\n\n/**\n * Checks whether a number falls within `[min, max]` (both inclusive by default).\n *\n * @param value - The number to test\n * @param min - Lower bound\n * @param max - Upper bound\n * @param options - Boundary inclusion mode (default: `'both'`)\n * @returns `true` if `value` is within the specified range\n * @example\n * inRange(5, 1, 10) // => true\n * inRange(1, 1, 10) // => true (min inclusive)\n * inRange(1, 1, 10, { inclusive: 'none' }) // => false\n * inRange(10, 1, 10, { inclusive: 'max' }) // => true\n * @since 2.0.0\n */\nexport function inRange(value: number, min: number, max: number, options: InRangeOptions = {}): boolean {\n const { inclusive = 'both' } = options;\n const minOk = inclusive === 'both' || inclusive === 'min' ? value >= min : value > min;\n const maxOk = inclusive === 'both' || inclusive === 'max' ? value <= max : value < max;\n return minOk && maxOk;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Checks if a value is an even integer.\n *\n * Returns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and odd integers.\n *\n * @param value - The value to check\n * @returns `true` if value is an integer divisible by 2\n * @example\n * isEven(2) // => true\n * isEven(0) // => true\n * isEven(-4) // => true\n * isEven(3) // => false\n * isEven(1.5) // => false (not an integer)\n * isEven('2') // => false\n * @since 2.0.3\n */\nexport function isEven(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value % 2 === 0;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Checks if a value is a number less than 0.\n *\n * Returns `false` for `NaN`, `0`, positive numbers, and non-number types.\n *\n * @param value - The value to check\n * @returns True if value is a negative number\n * @example\n * isNegative(-1) // => true\n * isNegative(-0.5) // => true\n * isNegative(-Infinity) // => true\n * isNegative(0) // => false\n * isNegative(1) // => false\n * isNegative(NaN) // => false\n * @since 2.0.0\n */\nexport function isNegative(value: unknown): value is number {\n return typeof value === 'number' && value < 0;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Checks if a value is an odd integer.\n *\n * Returns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and even integers.\n *\n * @param value - The value to check\n * @returns `true` if value is an integer not divisible by 2\n * @example\n * isOdd(3) // => true\n * isOdd(1) // => true\n * isOdd(-7) // => true\n * isOdd(2) // => false\n * isOdd(1.5) // => false (not an integer)\n * isOdd('3') // => false\n * @since 2.0.3\n */\nexport function isOdd(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value % 2 !== 0;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Checks if a value is a number greater than 0.\n *\n * Returns `false` for `NaN`, `0`, negative numbers, and non-number types.\n *\n * @param value - The value to check\n * @returns True if value is a positive number\n * @example\n * isPositive(42) // => true\n * isPositive(0.1) // => true\n * isPositive(Infinity) // => true\n * isPositive(0) // => false\n * isPositive(-1) // => false\n * isPositive(NaN) // => false\n * @since 2.0.0\n */\nexport function isPositive(value: unknown): value is number {\n return typeof value === 'number' && value > 0;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Linearly interpolates between `start` and `end` by the factor `t`.\n *\n * - `t = 0` returns `start`.\n * - `t = 1` returns `end`.\n * - Values of `t` outside `[0, 1]` extrapolate beyond the range.\n *\n * @param start - The start value.\n * @param end - The end value.\n * @param t - The interpolation factor.\n * @returns The interpolated value.\n * @example\n * lerp(0, 100, 0); // 0\n * lerp(0, 100, 1); // 100\n * lerp(0, 100, 0.5); // 50\n * lerp(0, 100, 0.25); // 25\n * @since 2.0.0\n */\nexport function lerp(start: number, end: number, t: number): number {\n if (t === 0) return start;\n if (t === 1) return end;\n if (Object.is(start, end)) return start;\n return start + (end - start) * t;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Calculates the arithmetic mean (average) of an array of numbers.\n * Returns `NaN` for an empty array.\n *\n * Pairs with {@link sum} for aggregate operations.\n *\n * @param array - The array of numbers to average\n * @returns The arithmetic mean, or `NaN` if the array is empty\n * @example\n * mean([1, 2, 3, 4]) // => 2.5\n * mean([10]) // => 10\n * mean([]) // => NaN\n * @since 2.0.0\n */\nexport function mean(array: readonly number[]): number {\n if (array.length === 0) return NaN;\n return array.reduce((acc, n) => acc + n, 0) / array.length;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Generates a random number between min and max (inclusive)\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Random number between min and max\n * @since 1.9.0\n */\nexport function randomBetween(min: number, max: number): number {\n return Math.random() * (max - min) + min;\n}\n\n/**\n * Generates a random integer between min and max (inclusive)\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Random integer between min and max\n * @since 1.9.0\n */\nexport function randomIntBetween(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Rounds a number to specified decimal places\n * @param value - The number to round\n * @param decimals - Number of decimal places\n * @returns Rounded number\n * @since 1.9.0\n */\nexport function roundTo(value: number, decimals: number): number {\n const multiplier = Math.pow(10, decimals);\n return Math.round(value * multiplier) / multiplier;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Calculates the sum of an array of numbers.\n * @param array - The array of numbers to sum\n * @returns The sum of all values\n * @example\n * sum([1, 2, 3, 4])\n * // => 10\n * @since 2.0.0\n */\nexport function sum(array: readonly number[]): number {\n return array.reduce((acc, n) => acc + n, 0);\n}\n"],"mappings":"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"}
package/llms.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  # @helpers4/number
2
2
 
3
3
  > Tree-shakable TypeScript utility functions for the `number` domain.
4
- > Package: `@helpers4/number` — Version: 2.0.1
4
+ > Package: `@helpers4/number` — Version: 2.0.3
5
5
  > License: LGPL-3.0-or-later
6
6
 
7
7
  ## Installation
@@ -15,7 +15,7 @@ pnpm add @helpers4/number
15
15
  ## Usage
16
16
 
17
17
  ```typescript
18
- import { clamp, formatCompact, formatSize, ... } from '@helpers4/number';
18
+ import { clamp, correctFloat, extractNumber, ... } from '@helpers4/number';
19
19
  ```
20
20
 
21
21
  ## Functions
@@ -23,9 +23,15 @@ import { clamp, formatCompact, formatSize, ... } from '@helpers4/number';
23
23
  | Function | Description |
24
24
  |---|---|
25
25
  | `clamp` | Clamps a number between min and max values |
26
+ | `correctFloat` | Corrects floating-point arithmetic errors by rounding to a given number of significant digits. Usefu |
27
+ | `extractNumber` | Extracts the first number embedded anywhere in a string, or passes through a `number`. Unlike a pla |
26
28
  | `formatCompact` | Formats a number using compact notation (e.g. `1_500_000 → "1.5M"`). Thin wrapper over `Intl.Number |
27
29
  | `formatSize` | Format a byte count into a human-readable string with the appropriate unit. Each unit is 1024 of th |
28
30
  | `inRange` | Checks whether a number falls within `[min, max]` (both inclusive by default). |
31
+ | `isEven` | Checks if a value is an even integer. Returns `false` for non-numbers, non-integers, `NaN`, `Infini |
32
+ | `isNegative` | Checks if a value is a number less than 0. Returns `false` for `NaN`, `0`, positive numbers, and no |
33
+ | `isOdd` | Checks if a value is an odd integer. Returns `false` for non-numbers, non-integers, `NaN`, `Infinit |
34
+ | `isPositive` | Checks if a value is a number greater than 0. Returns `false` for `NaN`, `0`, negative numbers, and |
29
35
  | `lerp` | Linearly interpolates between `start` and `end` by the factor `t`. - `t = 0` returns `start`. - `t |
30
36
  | `mean` | Calculates the arithmetic mean (average) of an array of numbers. Returns `NaN` for an empty array. |
31
37
  | `randomBetween` | Generates a random number between min and max (inclusive) |
@@ -69,6 +75,98 @@ clamp(5, 0, 10) // => 5
69
75
 
70
76
  ---
71
77
 
78
+ ### `correctFloat`
79
+
80
+ Corrects floating-point arithmetic errors by rounding to a given number
81
+ of significant digits. Useful after calculations that accumulate binary
82
+ floating-point drift (e.g. `0.1 + 0.2 === 0.30000000000000004`).
83
+
84
+ The default precision of 14 significant digits eliminates typical
85
+ rounding noise for values in the range used by most applications.
86
+ Note: for values whose integer part already consumes 14 or more digits
87
+ (i.e. |value| ≥ 1e13), toPrecision(14) has no room left for decimal
88
+ digits and will silently truncate them. Increase `precision` if you
89
+ need to correct drift in very large numbers.
90
+
91
+ ```typescript
92
+ import { correctFloat } from '@helpers4/number';
93
+
94
+ correctFloat(value: number, precision: number): number
95
+ ```
96
+
97
+ **Parameters:**
98
+
99
+ - `value: number` — The floating-point value to correct
100
+ - `precision: number` (default: `14`) — Integer number of significant digits (default: 14)
101
+
102
+ **Returns:** `number` — The corrected value
103
+
104
+ **Examples:**
105
+
106
+ *Fix floating-point drift*
107
+
108
+ Corrects the classic 0.1 + 0.2 floating-point arithmetic error.
109
+
110
+ ```typescript
111
+ 0.1 + 0.2 // => 0.30000000000000004
112
+ correctFloat(0.1 + 0.2) // => 0.3
113
+ ```
114
+
115
+ *Custom significant-digit precision*
116
+
117
+ Pass a second argument to control how many significant digits to keep.
118
+
119
+ ```typescript
120
+ correctFloat(1.23456789, 4) // => 1.235
121
+ correctFloat(1.23456789, 6) // => 1.23457
122
+ ```
123
+
124
+ ---
125
+
126
+ ### `extractNumber`
127
+
128
+ Extracts the first number embedded anywhere in a string, or passes through a `number`.
129
+
130
+ Unlike a plain `parseFloat`/`parseInt`, the number does not need to be at the start of
131
+ the string: digits are searched for anywhere, so leading/trailing text (units, labels, ...)
132
+ is ignored. A `-` before the digits and a scientific-notation suffix (`e`/`E`) are
133
+ disambiguated with ExtractNumberOptions.sign and ExtractNumberOptions.exponent.
134
+
135
+ Returns `undefined` if no number can be found.
136
+
137
+ ```typescript
138
+ import { extractNumber } from '@helpers4/number';
139
+
140
+ extractNumber(value: unknown, options: ExtractNumberOptions): number | undefined
141
+ ```
142
+
143
+ **Parameters:**
144
+
145
+ - `value: unknown` — The value to extract a number from
146
+ - `options: ExtractNumberOptions` (default: `{}`) — Options controlling sign and exponent disambiguation
147
+
148
+ **Returns:** `number | undefined` — The extracted number, or `undefined` if none was found
149
+
150
+ **Examples:**
151
+
152
+ *extractNumber*
153
+
154
+ ```typescript
155
+ ```ts
156
+ extractNumber('16.5px') // => 16.5
157
+ extractNumber('Wafer 10') // => 10
158
+ extractNumber('xxx-111') // => 111 ('-' glued to text → separator)
159
+ extractNumber('xxx -111') // => -111 ('-' preceded by a space → sign)
160
+ extractNumber('-111') // => -111 ('-' at the start of the string → sign)
161
+ extractNumber('1e5 mol') // => 100000
162
+ extractNumber('1e5kg') // => 1 ('e5' glued to text → mantissa only)
163
+ extractNumber('no number') // => undefined
164
+ extractNumber(42) // => 42
165
+ ```
166
+ ```
167
+
168
+ ---
169
+
72
170
  ### `formatCompact`
73
171
 
74
172
  Formats a number using compact notation (e.g. `1_500_000 → "1.5M"`).
@@ -190,6 +288,163 @@ inRange(10, 1, 10, { inclusive: 'none' }) // => false
190
288
 
191
289
  ---
192
290
 
291
+ ### `isEven`
292
+
293
+ Checks if a value is an even integer.
294
+
295
+ Returns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and odd integers.
296
+
297
+ ```typescript
298
+ import { isEven } from '@helpers4/number';
299
+
300
+ isEven(value: unknown): value is number
301
+ ```
302
+
303
+ **Parameters:**
304
+
305
+ - `value: unknown` — The value to check
306
+
307
+ **Returns:** `value is number` — `true` if value is an integer divisible by 2
308
+
309
+ **Examples:**
310
+
311
+ *Check if a number is even*
312
+
313
+ Returns true for integers divisible by 2, false otherwise.
314
+
315
+ ```typescript
316
+ isEven(4) // => true
317
+ isEven(0) // => true
318
+ isEven(3) // => false
319
+ isEven(1.5) // => false (not an integer)
320
+ ```
321
+
322
+ *Filter even numbers from an array*
323
+
324
+ Use as a predicate in .filter() to extract even integers.
325
+
326
+ ```typescript
327
+ const nums = [1, 2, 3, 4, 5, 6];
328
+ nums.filter(isEven)
329
+ // => [2, 4, 6]
330
+ ```
331
+
332
+ ---
333
+
334
+ ### `isNegative`
335
+
336
+ Checks if a value is a number less than 0.
337
+
338
+ Returns `false` for `NaN`, `0`, positive numbers, and non-number types.
339
+
340
+ ```typescript
341
+ import { isNegative } from '@helpers4/number';
342
+
343
+ isNegative(value: unknown): value is number
344
+ ```
345
+
346
+ **Parameters:**
347
+
348
+ - `value: unknown` — The value to check
349
+
350
+ **Returns:** `value is number` — True if value is a negative number
351
+
352
+ **Examples:**
353
+
354
+ *isNegative*
355
+
356
+ ```typescript
357
+ ```ts
358
+ isNegative(-1) // => true
359
+ isNegative(-0.5) // => true
360
+ isNegative(-Infinity) // => true
361
+ isNegative(0) // => false
362
+ isNegative(1) // => false
363
+ isNegative(NaN) // => false
364
+ ```
365
+ ```
366
+
367
+ ---
368
+
369
+ ### `isOdd`
370
+
371
+ Checks if a value is an odd integer.
372
+
373
+ Returns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and even integers.
374
+
375
+ ```typescript
376
+ import { isOdd } from '@helpers4/number';
377
+
378
+ isOdd(value: unknown): value is number
379
+ ```
380
+
381
+ **Parameters:**
382
+
383
+ - `value: unknown` — The value to check
384
+
385
+ **Returns:** `value is number` — `true` if value is an integer not divisible by 2
386
+
387
+ **Examples:**
388
+
389
+ *Check if a number is odd*
390
+
391
+ Returns true for integers not divisible by 2, false otherwise.
392
+
393
+ ```typescript
394
+ isOdd(3) // => true
395
+ isOdd(1) // => true
396
+ isOdd(2) // => false
397
+ isOdd(0) // => false
398
+ isOdd(1.5) // => false (not an integer)
399
+ ```
400
+
401
+ *Filter odd numbers from an array*
402
+
403
+ Use as a predicate in .filter() to extract odd integers.
404
+
405
+ ```typescript
406
+ const nums = [1, 2, 3, 4, 5, 6];
407
+ nums.filter(isOdd)
408
+ // => [1, 3, 5]
409
+ ```
410
+
411
+ ---
412
+
413
+ ### `isPositive`
414
+
415
+ Checks if a value is a number greater than 0.
416
+
417
+ Returns `false` for `NaN`, `0`, negative numbers, and non-number types.
418
+
419
+ ```typescript
420
+ import { isPositive } from '@helpers4/number';
421
+
422
+ isPositive(value: unknown): value is number
423
+ ```
424
+
425
+ **Parameters:**
426
+
427
+ - `value: unknown` — The value to check
428
+
429
+ **Returns:** `value is number` — True if value is a positive number
430
+
431
+ **Examples:**
432
+
433
+ *isPositive*
434
+
435
+ ```typescript
436
+ ```ts
437
+ isPositive(42) // => true
438
+ isPositive(0.1) // => true
439
+ isPositive(Infinity) // => true
440
+ isPositive(0) // => false
441
+ isPositive(-1) // => false
442
+ isPositive(NaN) // => false
443
+ ```
444
+ ```
445
+
446
+ ---
447
+
193
448
  ### `lerp`
194
449
 
195
450
  Linearly interpolates between `start` and `end` by the factor `t`.
package/meta/api.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "category": "number",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "functions": [
5
5
  {
6
6
  "name": "clamp",
@@ -43,6 +43,85 @@
43
43
  ],
44
44
  "sourceFile": "clamp.ts"
45
45
  },
46
+ {
47
+ "name": "correctFloat",
48
+ "kind": "function",
49
+ "description": "Corrects floating-point arithmetic errors by rounding to a given number\nof significant digits. Useful after calculations that accumulate binary\nfloating-point drift (e.g. `0.1 + 0.2 === 0.30000000000000004`).\n\nThe default precision of 14 significant digits eliminates typical\nrounding noise for values in the range used by most applications.\nNote: for values whose integer part already consumes 14 or more digits\n(i.e. |value| ≥ 1e13), toPrecision(14) has no room left for decimal\ndigits and will silently truncate them. Increase `precision` if you\nneed to correct drift in very large numbers.",
50
+ "since": "2.0.2",
51
+ "signatures": [
52
+ {
53
+ "signature": "correctFloat(value: number, precision: number): number",
54
+ "description": "Corrects floating-point arithmetic errors by rounding to a given number\nof significant digits. Useful after calculations that accumulate binary\nfloating-point drift (e.g. `0.1 + 0.2 === 0.30000000000000004`).\n\nThe default precision of 14 significant digits eliminates typical\nrounding noise for values in the range used by most applications.\nNote: for values whose integer part already consumes 14 or more digits\n(i.e. |value| ≥ 1e13), toPrecision(14) has no room left for decimal\ndigits and will silently truncate them. Increase `precision` if you\nneed to correct drift in very large numbers.",
55
+ "params": [
56
+ {
57
+ "name": "value",
58
+ "type": "number",
59
+ "description": "The floating-point value to correct"
60
+ },
61
+ {
62
+ "name": "precision",
63
+ "type": "number",
64
+ "description": "Integer number of significant digits (default: 14)",
65
+ "defaultValue": "14"
66
+ }
67
+ ],
68
+ "returns": {
69
+ "type": "number",
70
+ "description": "The corrected value"
71
+ }
72
+ }
73
+ ],
74
+ "examples": [
75
+ {
76
+ "title": "Fix floating-point drift",
77
+ "description": "Corrects the classic 0.1 + 0.2 floating-point arithmetic error.",
78
+ "code": "0.1 + 0.2 // => 0.30000000000000004\ncorrectFloat(0.1 + 0.2) // => 0.3"
79
+ },
80
+ {
81
+ "title": "Custom significant-digit precision",
82
+ "description": "Pass a second argument to control how many significant digits to keep.",
83
+ "code": "correctFloat(1.23456789, 4) // => 1.235\ncorrectFloat(1.23456789, 6) // => 1.23457"
84
+ }
85
+ ],
86
+ "sourceFile": "correctFloat.ts"
87
+ },
88
+ {
89
+ "name": "extractNumber",
90
+ "kind": "function",
91
+ "description": "Extracts the first number embedded anywhere in a string, or passes through a `number`.\n\nUnlike a plain `parseFloat`/`parseInt`, the number does not need to be at the start of\nthe string: digits are searched for anywhere, so leading/trailing text (units, labels, ...)\nis ignored. A `-` before the digits and a scientific-notation suffix (`e`/`E`) are\ndisambiguated with ExtractNumberOptions.sign and ExtractNumberOptions.exponent.\n\nReturns `undefined` if no number can be found.",
92
+ "since": "2.0.3",
93
+ "signatures": [
94
+ {
95
+ "signature": "extractNumber(value: unknown, options: ExtractNumberOptions): number | undefined",
96
+ "description": "Extracts the first number embedded anywhere in a string, or passes through a `number`.\n\nUnlike a plain `parseFloat`/`parseInt`, the number does not need to be at the start of\nthe string: digits are searched for anywhere, so leading/trailing text (units, labels, ...)\nis ignored. A `-` before the digits and a scientific-notation suffix (`e`/`E`) are\ndisambiguated with ExtractNumberOptions.sign and ExtractNumberOptions.exponent.\n\nReturns `undefined` if no number can be found.",
97
+ "params": [
98
+ {
99
+ "name": "value",
100
+ "type": "unknown",
101
+ "description": "The value to extract a number from"
102
+ },
103
+ {
104
+ "name": "options",
105
+ "type": "ExtractNumberOptions",
106
+ "description": "Options controlling sign and exponent disambiguation",
107
+ "defaultValue": "{}"
108
+ }
109
+ ],
110
+ "returns": {
111
+ "type": "number | undefined",
112
+ "description": "The extracted number, or `undefined` if none was found"
113
+ }
114
+ }
115
+ ],
116
+ "examples": [
117
+ {
118
+ "title": "extractNumber",
119
+ "description": "",
120
+ "code": "```ts\nextractNumber('16.5px') // => 16.5\nextractNumber('Wafer 10') // => 10\nextractNumber('xxx-111') // => 111 ('-' glued to text → separator)\nextractNumber('xxx -111') // => -111 ('-' preceded by a space → sign)\nextractNumber('-111') // => -111 ('-' at the start of the string → sign)\nextractNumber('1e5 mol') // => 100000\nextractNumber('1e5kg') // => 1 ('e5' glued to text → mantissa only)\nextractNumber('no number') // => undefined\nextractNumber(42) // => 42\n```"
121
+ }
122
+ ],
123
+ "sourceFile": "extractNumber.ts"
124
+ },
46
125
  {
47
126
  "name": "formatCompact",
48
127
  "kind": "function",
@@ -166,14 +245,141 @@
166
245
  "code": "inRange(5, 1, 10, { inclusive: 'none' }) // => true\ninRange(1, 1, 10, { inclusive: 'none' }) // => false\ninRange(10, 1, 10, { inclusive: 'none' }) // => false"
167
246
  }
168
247
  ],
169
- "sourceFile": "inRange.ts",
170
- "relatedTypes": [
248
+ "sourceFile": "inRange.ts"
249
+ },
250
+ {
251
+ "name": "isEven",
252
+ "kind": "function",
253
+ "description": "Checks if a value is an even integer.\n\nReturns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and odd integers.",
254
+ "since": "2.0.3",
255
+ "signatures": [
171
256
  {
172
- "name": "InRangeOptions",
173
- "description": "Options for inRange.",
174
- "typeDefinition": "interface InRangeOptions {\n inclusive?: \"min\" | \"max\" | \"both\" | \"none\";\n}"
257
+ "signature": "isEven(value: unknown): value is number",
258
+ "description": "Checks if a value is an even integer.\n\nReturns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and odd integers.",
259
+ "params": [
260
+ {
261
+ "name": "value",
262
+ "type": "unknown",
263
+ "description": "The value to check"
264
+ }
265
+ ],
266
+ "returns": {
267
+ "type": "value is number",
268
+ "description": "`true` if value is an integer divisible by 2"
269
+ }
175
270
  }
176
- ]
271
+ ],
272
+ "examples": [
273
+ {
274
+ "title": "Check if a number is even",
275
+ "description": "Returns true for integers divisible by 2, false otherwise.",
276
+ "code": "isEven(4) // => true\nisEven(0) // => true\nisEven(3) // => false\nisEven(1.5) // => false (not an integer)"
277
+ },
278
+ {
279
+ "title": "Filter even numbers from an array",
280
+ "description": "Use as a predicate in .filter() to extract even integers.",
281
+ "code": "const nums = [1, 2, 3, 4, 5, 6];\nnums.filter(isEven)\n// => [2, 4, 6]"
282
+ }
283
+ ],
284
+ "sourceFile": "isEven.ts"
285
+ },
286
+ {
287
+ "name": "isNegative",
288
+ "kind": "function",
289
+ "description": "Checks if a value is a number less than 0.\n\nReturns `false` for `NaN`, `0`, positive numbers, and non-number types.",
290
+ "since": "2.0.0",
291
+ "signatures": [
292
+ {
293
+ "signature": "isNegative(value: unknown): value is number",
294
+ "description": "Checks if a value is a number less than 0.\n\nReturns `false` for `NaN`, `0`, positive numbers, and non-number types.",
295
+ "params": [
296
+ {
297
+ "name": "value",
298
+ "type": "unknown",
299
+ "description": "The value to check"
300
+ }
301
+ ],
302
+ "returns": {
303
+ "type": "value is number",
304
+ "description": "True if value is a negative number"
305
+ }
306
+ }
307
+ ],
308
+ "examples": [
309
+ {
310
+ "title": "isNegative",
311
+ "description": "",
312
+ "code": "```ts\nisNegative(-1) // => true\nisNegative(-0.5) // => true\nisNegative(-Infinity) // => true\nisNegative(0) // => false\nisNegative(1) // => false\nisNegative(NaN) // => false\n```"
313
+ }
314
+ ],
315
+ "sourceFile": "isNegative.ts"
316
+ },
317
+ {
318
+ "name": "isOdd",
319
+ "kind": "function",
320
+ "description": "Checks if a value is an odd integer.\n\nReturns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and even integers.",
321
+ "since": "2.0.3",
322
+ "signatures": [
323
+ {
324
+ "signature": "isOdd(value: unknown): value is number",
325
+ "description": "Checks if a value is an odd integer.\n\nReturns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and even integers.",
326
+ "params": [
327
+ {
328
+ "name": "value",
329
+ "type": "unknown",
330
+ "description": "The value to check"
331
+ }
332
+ ],
333
+ "returns": {
334
+ "type": "value is number",
335
+ "description": "`true` if value is an integer not divisible by 2"
336
+ }
337
+ }
338
+ ],
339
+ "examples": [
340
+ {
341
+ "title": "Check if a number is odd",
342
+ "description": "Returns true for integers not divisible by 2, false otherwise.",
343
+ "code": "isOdd(3) // => true\nisOdd(1) // => true\nisOdd(2) // => false\nisOdd(0) // => false\nisOdd(1.5) // => false (not an integer)"
344
+ },
345
+ {
346
+ "title": "Filter odd numbers from an array",
347
+ "description": "Use as a predicate in .filter() to extract odd integers.",
348
+ "code": "const nums = [1, 2, 3, 4, 5, 6];\nnums.filter(isOdd)\n// => [1, 3, 5]"
349
+ }
350
+ ],
351
+ "sourceFile": "isOdd.ts"
352
+ },
353
+ {
354
+ "name": "isPositive",
355
+ "kind": "function",
356
+ "description": "Checks if a value is a number greater than 0.\n\nReturns `false` for `NaN`, `0`, negative numbers, and non-number types.",
357
+ "since": "2.0.0",
358
+ "signatures": [
359
+ {
360
+ "signature": "isPositive(value: unknown): value is number",
361
+ "description": "Checks if a value is a number greater than 0.\n\nReturns `false` for `NaN`, `0`, negative numbers, and non-number types.",
362
+ "params": [
363
+ {
364
+ "name": "value",
365
+ "type": "unknown",
366
+ "description": "The value to check"
367
+ }
368
+ ],
369
+ "returns": {
370
+ "type": "value is number",
371
+ "description": "True if value is a positive number"
372
+ }
373
+ }
374
+ ],
375
+ "examples": [
376
+ {
377
+ "title": "isPositive",
378
+ "description": "",
379
+ "code": "```ts\nisPositive(42) // => true\nisPositive(0.1) // => true\nisPositive(Infinity) // => true\nisPositive(0) // => false\nisPositive(-1) // => false\nisPositive(NaN) // => false\n```"
380
+ }
381
+ ],
382
+ "sourceFile": "isPositive.ts"
177
383
  },
178
384
  {
179
385
  "name": "lerp",
@@ -11,6 +11,31 @@
11
11
  }
12
12
  ]
13
13
  },
14
+ {
15
+ "name": "correctFloat",
16
+ "examples": [
17
+ {
18
+ "title": "Fix floating-point drift",
19
+ "description": "Corrects the classic 0.1 + 0.2 floating-point arithmetic error.",
20
+ "code": "0.1 + 0.2 // => 0.30000000000000004\ncorrectFloat(0.1 + 0.2) // => 0.3"
21
+ },
22
+ {
23
+ "title": "Custom significant-digit precision",
24
+ "description": "Pass a second argument to control how many significant digits to keep.",
25
+ "code": "correctFloat(1.23456789, 4) // => 1.235\ncorrectFloat(1.23456789, 6) // => 1.23457"
26
+ }
27
+ ]
28
+ },
29
+ {
30
+ "name": "extractNumber",
31
+ "examples": [
32
+ {
33
+ "title": "extractNumber",
34
+ "description": "",
35
+ "code": "```ts\nextractNumber('16.5px') // => 16.5\nextractNumber('Wafer 10') // => 10\nextractNumber('xxx-111') // => 111 ('-' glued to text → separator)\nextractNumber('xxx -111') // => -111 ('-' preceded by a space → sign)\nextractNumber('-111') // => -111 ('-' at the start of the string → sign)\nextractNumber('1e5 mol') // => 100000\nextractNumber('1e5kg') // => 1 ('e5' glued to text → mantissa only)\nextractNumber('no number') // => undefined\nextractNumber(42) // => 42\n```"
36
+ }
37
+ ]
38
+ },
14
39
  {
15
40
  "name": "formatCompact",
16
41
  "examples": [
@@ -51,6 +76,56 @@
51
76
  }
52
77
  ]
53
78
  },
79
+ {
80
+ "name": "isEven",
81
+ "examples": [
82
+ {
83
+ "title": "Check if a number is even",
84
+ "description": "Returns true for integers divisible by 2, false otherwise.",
85
+ "code": "isEven(4) // => true\nisEven(0) // => true\nisEven(3) // => false\nisEven(1.5) // => false (not an integer)"
86
+ },
87
+ {
88
+ "title": "Filter even numbers from an array",
89
+ "description": "Use as a predicate in .filter() to extract even integers.",
90
+ "code": "const nums = [1, 2, 3, 4, 5, 6];\nnums.filter(isEven)\n// => [2, 4, 6]"
91
+ }
92
+ ]
93
+ },
94
+ {
95
+ "name": "isNegative",
96
+ "examples": [
97
+ {
98
+ "title": "isNegative",
99
+ "description": "",
100
+ "code": "```ts\nisNegative(-1) // => true\nisNegative(-0.5) // => true\nisNegative(-Infinity) // => true\nisNegative(0) // => false\nisNegative(1) // => false\nisNegative(NaN) // => false\n```"
101
+ }
102
+ ]
103
+ },
104
+ {
105
+ "name": "isOdd",
106
+ "examples": [
107
+ {
108
+ "title": "Check if a number is odd",
109
+ "description": "Returns true for integers not divisible by 2, false otherwise.",
110
+ "code": "isOdd(3) // => true\nisOdd(1) // => true\nisOdd(2) // => false\nisOdd(0) // => false\nisOdd(1.5) // => false (not an integer)"
111
+ },
112
+ {
113
+ "title": "Filter odd numbers from an array",
114
+ "description": "Use as a predicate in .filter() to extract odd integers.",
115
+ "code": "const nums = [1, 2, 3, 4, 5, 6];\nnums.filter(isOdd)\n// => [1, 3, 5]"
116
+ }
117
+ ]
118
+ },
119
+ {
120
+ "name": "isPositive",
121
+ "examples": [
122
+ {
123
+ "title": "isPositive",
124
+ "description": "",
125
+ "code": "```ts\nisPositive(42) // => true\nisPositive(0.1) // => true\nisPositive(Infinity) // => true\nisPositive(0) // => false\nisPositive(-1) // => false\nisPositive(NaN) // => false\n```"
126
+ }
127
+ ]
128
+ },
54
129
  {
55
130
  "name": "lerp",
56
131
  "examples": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@helpers4/number",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "A set of helpers in TS/JS, compatible with tree-shaking, for number.",
5
5
  "author": "baxyz <baxy@etik.com>",
6
6
  "license": "LGPL-3.0",
@@ -28,9 +28,15 @@
28
28
  "helpers",
29
29
  "number",
30
30
  "clamp",
31
+ "correctFloat",
32
+ "extractNumber",
31
33
  "formatCompact",
32
34
  "formatSize",
33
35
  "inRange",
36
+ "isEven",
37
+ "isNegative",
38
+ "isOdd",
39
+ "isPositive",
34
40
  "lerp",
35
41
  "mean",
36
42
  "random",