# @helpers4/number

> Tree-shakable TypeScript utility functions for the `number` domain.
> Package: `@helpers4/number` — Version: 2.1.0
> License: LGPL-3.0-or-later

## Installation

```sh
npm install @helpers4/number
# or
pnpm add @helpers4/number
```

## Usage

```typescript
import { clamp, correctFloat, extractNumber, ... } from '@helpers4/number';
```

## Functions

| Function | Description |
|---|---|
| `clamp` | Clamps a number between min and max values |
| `correctFloat` | Corrects floating-point arithmetic errors by rounding to a given number of significant digits. Usefu |
| `extractNumber` | Extracts the first number embedded anywhere in a string, or passes through a `number`.  Unlike a pla |
| `formatCompact` | Formats a number using compact notation (e.g. `1_500_000 → "1.5M"`).  Thin wrapper over `Intl.Number |
| `formatSize` | Format a byte count into a human-readable string with the appropriate unit.  Each unit is 1024 of th |
| `inRange` | Checks whether a number falls within `[min, max]` (both inclusive by default). |
| `isEven` | Checks if a value is an even integer.  Returns `false` for non-numbers, non-integers, `NaN`, `Infini |
| `isNegative` | Checks if a value is a number less than 0.  Returns `false` for `NaN`, `0`, positive numbers, and no |
| `isOdd` | Checks if a value is an odd integer.  Returns `false` for non-numbers, non-integers, `NaN`, `Infinit |
| `isPositive` | Checks if a value is a number greater than 0.  Returns `false` for `NaN`, `0`, negative numbers, and |
| `lerp` | Linearly interpolates between `start` and `end` by the factor `t`.  - `t = 0` returns `start`. - `t  |
| `randomBetween` | Generates a random number between min and max (inclusive) |
| `randomIntBetween` | Generates a random integer between min and max (inclusive) |
| `roundTo` | Rounds a number to specified decimal places |

---

## API Reference

### `clamp`

Clamps a number between min and max values

```typescript
import { clamp } from '@helpers4/number';

clamp(value: number, min: number, max: number): number
```

**Parameters:**

- `value: number` — The value to clamp
- `min: number` — Minimum value
- `max: number` — Maximum value

**Returns:** `number` — Clamped value

**Examples:**

*Clamp a value within range*

Restricts a number to be within a min/max range.

```typescript
clamp(15, 0, 10)  // => 10
clamp(-5, 0, 10)  // => 0
clamp(5, 0, 10)   // => 5
```

---

### `correctFloat`

Corrects floating-point arithmetic errors by rounding to a given number
of significant digits. Useful after calculations that accumulate binary
floating-point drift (e.g. `0.1 + 0.2 === 0.30000000000000004`).

The default precision of 14 significant digits eliminates typical
rounding noise for values in the range used by most applications.
Note: for values whose integer part already consumes 14 or more digits
(i.e. |value| ≥ 1e13), toPrecision(14) has no room left for decimal
digits and will silently truncate them. Increase `precision` if you
need to correct drift in very large numbers.

Note: IEEE-754 doubles carry at most ~17 significant decimal digits.
Precision values above 17 pad with digits that reflect the underlying
binary representation rather than correcting drift.

```typescript
import { correctFloat } from '@helpers4/number';

correctFloat(value: number, precision: number): number
```

**Parameters:**

- `value: number` — The floating-point value to correct
- `precision: number` (default: `14`) — Integer number of significant digits between 1 and 100
  (default: 14). Values above 17 are valid but expose binary noise beyond
  IEEE-754's meaningful range.

**Returns:** `number` — The corrected value

**Examples:**

*Fix floating-point drift*

Corrects the classic 0.1 + 0.2 floating-point arithmetic error.

```typescript
0.1 + 0.2              // => 0.30000000000000004
correctFloat(0.1 + 0.2)  // => 0.3
```

*Custom significant-digit precision*

Pass a second argument to control how many significant digits to keep.

```typescript
correctFloat(1.23456789, 4)  // => 1.235
correctFloat(1.23456789, 6)  // => 1.23457
```

---

### `extractNumber`

Extracts the first number embedded anywhere in a string, or passes through a `number`.

Unlike a plain `parseFloat`/`parseInt`, the number does not need to be at the start of
the string: digits are searched for anywhere, so leading/trailing text (units, labels, ...)
is ignored. A `-` before the digits and a scientific-notation suffix (`e`/`E`) are
disambiguated with ExtractNumberOptions.sign and ExtractNumberOptions.exponent.

Returns `undefined` if no number can be found.

```typescript
import { extractNumber } from '@helpers4/number';

extractNumber(value: unknown, options: ExtractNumberOptions): number | undefined
```

**Parameters:**

- `value: unknown` — The value to extract a number from
- `options: ExtractNumberOptions` (default: `{}`) — Options controlling sign and exponent disambiguation

**Returns:** `number | undefined` — The extracted number, or `undefined` if none was found

**Examples:**

*extractNumber*

```typescript
```ts
extractNumber('16.5px')        // => 16.5
extractNumber('.5rem')         // => 0.5   (leading-dot decimal)
extractNumber('-.5')           // => -0.5  (leading-dot with sign)
extractNumber('Wafer 10')      // => 10
extractNumber('xxx-111')       // => 111   ('-' glued to text → separator)
extractNumber('xxx -111')      // => -111  ('-' preceded by a space → sign)
extractNumber('x-.5')          // => 0.5   ('-' glued to 'x' → separator; leading-dot decimal follows)
extractNumber('-111')          // => -111  ('-' at the start of the string → sign)
extractNumber('1e5 mol')       // => 100000
extractNumber('1e5kg')         // => 1     ('e5' glued to text → mantissa only)
extractNumber('no number')     // => undefined
extractNumber(42)              // => 42
```
```

---

### `formatCompact`

Formats a number using compact notation (e.g. `1_500_000 → "1.5M"`).

Thin wrapper over `Intl.NumberFormat` with `notation: 'compact'`. Companion
of `formatSize` in the same `format*` family.

```typescript
import { formatCompact } from '@helpers4/number';

formatCompact(value: number, locale?: string): string
```

**Parameters:**

- `value: number` — The number to format.
- `locale?: string` — BCP 47 locale tag. Defaults to the runtime locale.

**Returns:** `string` — A compact string representation of the number.

**Examples:**

*Compact large numbers*

Formats a number using K / M suffixes for readability.

```typescript
formatCompact(1_500_000, 'en') // => '1.5M'
formatCompact(1_000, 'en')     // => '1K'
formatCompact(999, 'en')       // => '999'
```

*Locale-aware formatting*

Uses the provided locale for the decimal separator and suffix.

```typescript
formatCompact(1_500_000, 'fr') // => '1,5 M'
```

---

### `formatSize`

Format a byte count into a human-readable string with the appropriate unit.

Each unit is 1024 of the previous (binary prefix). The result is formatted
with one decimal place.

```typescript
import { formatSize } from '@helpers4/number';

formatSize(bytes: number): string
```

**Parameters:**

- `bytes: number` — A non-negative integer representing a byte count.

**Returns:** `string` — A human-readable string such as `'0.0B'`, `'1.5KB'`, `'3.2MB'`.

**Examples:**

*Format bytes to human-readable size*

Converts a raw byte count to a human-readable string using binary prefixes.

```typescript
formatSize(0)             // '0.0B'
formatSize(512)           // '512.0B'
formatSize(1024)          // '1.0KB'
formatSize(1_048_576)     // '1.0MB'
formatSize(1_073_741_824) // '1.0GB'
```

---

### `inRange`

Checks whether a number falls within `[min, max]` (both inclusive by default).

```typescript
import { inRange } from '@helpers4/number';

inRange(value: number, min: number, max: number, options: InRangeOptions): boolean
```

**Parameters:**

- `value: number` — The number to test
- `min: number` — Lower bound
- `max: number` — Upper bound
- `options: InRangeOptions` (default: `{}`) — Boundary inclusion mode (default: `'both'`)

**Returns:** `boolean` — `true` if `value` is within the specified range

**Examples:**

*Check if a value is within bounds (inclusive)*

Both min and max are included by default.

```typescript
inRange(5, 1, 10)   // => true
inRange(0, 1, 10)   // => false
inRange(1, 1, 10)   // => true  (min included)
inRange(10, 1, 10)  // => true  (max included)
```

*Exclusive range*

Use { inclusive: "none" } for open interval (min, max).

```typescript
inRange(5, 1, 10, { inclusive: 'none' })  // => true
inRange(1, 1, 10, { inclusive: 'none' })  // => false
inRange(10, 1, 10, { inclusive: 'none' }) // => false
```

---

### `isEven`

Checks if a value is an even integer.

Returns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and odd integers.

```typescript
import { isEven } from '@helpers4/number';

isEven(value: unknown): value is number
```

**Parameters:**

- `value: unknown` — The value to check

**Returns:** `value is number` — `true` if value is an integer divisible by 2

**Examples:**

*Check if a number is even*

Returns true for integers divisible by 2, false otherwise.

```typescript
isEven(4)   // => true
isEven(0)   // => true
isEven(3)   // => false
isEven(1.5) // => false  (not an integer)
```

*Filter even numbers from an array*

Use as a predicate in .filter() to extract even integers.

```typescript
const nums = [1, 2, 3, 4, 5, 6];
nums.filter(isEven)
// => [2, 4, 6]
```

---

### `isNegative`

Checks if a value is a number less than 0.

Returns `false` for `NaN`, `0`, positive numbers, and non-number types.

```typescript
import { isNegative } from '@helpers4/number';

isNegative(value: unknown): value is number
```

**Parameters:**

- `value: unknown` — The value to check

**Returns:** `value is number` — True if value is a negative number

**Examples:**

*isNegative*

```typescript
```ts
isNegative(-1)        // => true
isNegative(-0.5)      // => true
isNegative(-Infinity) // => true
isNegative(0)         // => false
isNegative(1)         // => false
isNegative(NaN)       // => false
```
```

---

### `isOdd`

Checks if a value is an odd integer.

Returns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and even integers.

```typescript
import { isOdd } from '@helpers4/number';

isOdd(value: unknown): value is number
```

**Parameters:**

- `value: unknown` — The value to check

**Returns:** `value is number` — `true` if value is an integer not divisible by 2

**Examples:**

*Check if a number is odd*

Returns true for integers not divisible by 2, false otherwise.

```typescript
isOdd(3)   // => true
isOdd(1)   // => true
isOdd(2)   // => false
isOdd(0)   // => false
isOdd(1.5) // => false  (not an integer)
```

*Filter odd numbers from an array*

Use as a predicate in .filter() to extract odd integers.

```typescript
const nums = [1, 2, 3, 4, 5, 6];
nums.filter(isOdd)
// => [1, 3, 5]
```

---

### `isPositive`

Checks if a value is a number greater than 0.

Returns `false` for `NaN`, `0`, negative numbers, and non-number types.

```typescript
import { isPositive } from '@helpers4/number';

isPositive(value: unknown): value is number
```

**Parameters:**

- `value: unknown` — The value to check

**Returns:** `value is number` — True if value is a positive number

**Examples:**

*isPositive*

```typescript
```ts
isPositive(42)       // => true
isPositive(0.1)      // => true
isPositive(Infinity) // => true
isPositive(0)        // => false
isPositive(-1)       // => false
isPositive(NaN)      // => false
```
```

---

### `lerp`

Linearly interpolates between `start` and `end` by the factor `t`.

- `t = 0` returns `start`.
- `t = 1` returns `end`.
- Values of `t` outside `[0, 1]` extrapolate beyond the range.

```typescript
import { lerp } from '@helpers4/number';

lerp(start: number, end: number, t: number): number
```

**Parameters:**

- `start: number` — The start value.
- `end: number` — The end value.
- `t: number` — The interpolation factor.

**Returns:** `number` — The interpolated value.

**Examples:**

*Interpolate between two values*

Returns the value between start and end at position t (0 = start, 1 = end).

```typescript
lerp(0, 100, 0)    // => 0
lerp(0, 100, 0.5)  // => 50
lerp(0, 100, 1)    // => 100
```

*Animate a colour channel*

t outside [0, 1] extrapolates beyond the range.

```typescript
lerp(0, 255, 0.5) // => 127.5
lerp(0, 10, 2)    // => 20  (extrapolation)
```

---

### `randomBetween`

Generates a random number between min and max (inclusive)

```typescript
import { randomBetween } from '@helpers4/number';

randomBetween(min: number, max: number): number
```

**Parameters:**

- `min: number` — Minimum value
- `max: number` — Maximum value

**Returns:** `number` — Random number between min and max

**Examples:**

*Generate a random float in range*

Returns a random number between min and max (inclusive).

```typescript
randomBetween(1, 10)
// => e.g. 5.327...
```

*Generate a random integer in range*

Returns a random integer between min and max (inclusive).

```typescript
randomIntBetween(1, 6)
// => e.g. 4
```

---

### `randomIntBetween`

Generates a random integer between min and max (inclusive)

```typescript
import { randomIntBetween } from '@helpers4/number';

randomIntBetween(min: number, max: number): number
```

**Parameters:**

- `min: number` — Minimum value
- `max: number` — Maximum value

**Returns:** `number` — Random integer between min and max

---

### `roundTo`

Rounds a number to specified decimal places

```typescript
import { roundTo } from '@helpers4/number';

roundTo(value: number, decimals: number): number
```

**Parameters:**

- `value: number` — The number to round
- `decimals: number` — Number of decimal places

**Returns:** `number` — Rounded number

**Examples:**

*Round to 2 decimal places*

Rounds a floating-point number to the specified number of decimals.

```typescript
roundTo(3.14159, 2)
// => 3.14
```

*Round to 0 decimal places*

Effectively rounds to the nearest integer.

```typescript
roundTo(3.7, 0)
// => 4
```

---
