Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ These changes improve throughput and reduce memory pressure when working with la
- [formatList](#formatlist) - Formats an array of strings into a human-readable list with proper commas and "and".
- [formatToOctal](#formattotoctal) - Converts a decimal number to octal, optional "0o" prefix.
- [formatTemperature](#formattemperature) - Converts temperatures between Celsius, Fahrenheit, and Kelvin.
- [formatToBinary](#formattobinary) - Converts a decimal integer to a binary string with optional bit grouping.
- [formatToHexadecimal](#formattohexadecimal) - Converts temperatures between Celsius, Fahrenheit, and Kelvin.

- [formatToDecimal](#formattodecimal) - Converts base-2/8/16 strings to decimal.
Expand Down Expand Up @@ -1532,6 +1533,36 @@ Notes:
- Kelvin values are rendered without the degree symbol (e.g., "298.15K").
- An error is thrown for invalid conversions or non-numeric input values.

#### <a id="formattobinary"></a>`formatToBinary(num, options)`

Converts a decimal integer to its binary (base-2) string representation with optional grouping from the least significant bit for readability. Supports negative numbers.

```javascript
import { formatToBinary } from 'stringzy';

// Basic conversions
formatToBinary(5); // "101"
formatToBinary(10); // "1010"
formatToBinary(255); // "11111111"
formatToBinary(0); // "0"
formatToBinary(-5); // "-101"

// Grouping from right to left (no left-padding)
formatToBinary(255, { group: 4 }); // "1111 1111"
formatToBinary(10, { group: 2 }); // "10 10"
formatToBinary(-255, { group: 4 }); // "-1111 1111"

// Invalid cases
formatToBinary(3.14); // TypeError (must be an integer)
formatToBinary('5'); // TypeError (input must be a number)
formatToBinary(10, { group: 0 }); // TypeError (group must be positive integer)
```

| Parameter | Type | Default | Description |
| --------- | ------ | ------- | ------------------------------------------------ |
| num | number | required| The decimal integer to convert to binary. |
| options | object | `{}` | Optional configuration. |
| - group | number | — | Positive integer; bits per group (right-to-left) |
#### <a id="formattohexadecimal"></a>formatToHexadecimal(num, options)
Converts a decimal number into its hexadecimal (base-16) string representation.</br>
Supports optional prefix "0x" and lowercase formatting.</br>
Expand Down
57 changes: 57 additions & 0 deletions src/formatting/binary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Converts a decimal integer to its binary (base-2) string representation.
*
* - Supports negative numbers (prefixed with '-')
* - Optional grouping from the least significant bit for readability
*
* Examples:
* 5 → "101"
* 10 → "1010"
* 255 → "11111111"
* 0 → "0"
*
* Grouping examples (from right to left):
* formatToBinary(255, { group: 4 }) → "1111 1111"
* formatToBinary(10, { group: 2 }) → "10 10"
*
* @param {number} num - The decimal integer to convert.
* @param {{ group?: number }} [options] - Optional grouping configuration.
* @returns {string} The binary string representation.
* @throws {TypeError} If input is not a number, is NaN, or not an integer.
*/
export function formatToBinary(num: number, options?: { group?: number }): string {
if (typeof num !== 'number' || Number.isNaN(num)) {
throw new TypeError('Input must be a number');
}

if (!Number.isInteger(num)) {
throw new TypeError('Input must be an integer');
}

const isNegative = num < 0;
const absoluteValue = Math.abs(num);

// Handle zero explicitly to avoid "-0" or empty strings
const core = absoluteValue.toString(2);

const groupSize = options?.group;
if (groupSize !== undefined) {
if (
typeof groupSize !== 'number' ||
Number.isNaN(groupSize) ||
!Number.isInteger(groupSize) ||
groupSize <= 0
) {
throw new TypeError('Group size must be a positive integer');
}
}

const grouped =
groupSize && core.length > groupSize
? core.replace(new RegExp(`\\B(?=(\\d{${groupSize}})+(?!\\d))`, 'g'), ' ')
: core;

return isNegative ? `-${grouped}` : grouped;
}


5 changes: 4 additions & 1 deletion src/formatting/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export { formatOrdinal } from './ordinal';
export { formatList } from './listToString';
export { formatToOctal } from './octal';
export { formatTemperature } from './temperature';
export { formatToBinary } from './binary';
export { formatToHexadecimal } from './hexadecimal';
export { formatToDecimal } from './decimal';

Expand All @@ -25,6 +26,7 @@ import { formatOrdinal } from './ordinal';
import { formatList } from './listToString';
import { formatToOctal } from './octal';
import { formatTemperature } from './temperature';
import { formatToBinary } from './binary';
import { formatToHexadecimal } from './hexadecimal';
import { formatToDecimal } from './decimal';

Expand All @@ -40,7 +42,8 @@ export const formatting = {
formatOrdinal,
formatList,
formatTemperature,
formatToBinary,
formatToHexadecimal,
formatToOctal,
formatToDecimal,
formatToDecimal
};
59 changes: 59 additions & 0 deletions src/tests/formatting/binary.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { describe, it } from 'node:test';
import assert from 'node:assert';
import { formatToBinary } from '../../formatting/binary';

describe('formatToBinary', () => {
it('converts standard positive integers', () => {
assert.strictEqual(formatToBinary(5), '101');
assert.strictEqual(formatToBinary(10), '1010');
assert.strictEqual(formatToBinary(255), '11111111');
assert.strictEqual(formatToBinary(1), '1');
assert.strictEqual(formatToBinary(2), '10');
});

it('handles zero', () => {
assert.strictEqual(formatToBinary(0), '0');
});

it('prefixes negative numbers with a minus sign', () => {
assert.strictEqual(formatToBinary(-5), '-101');
assert.strictEqual(formatToBinary(-10), '-1010');
});

it('supports optional grouping from right to left (LSB first)', () => {
assert.strictEqual(formatToBinary(255, { group: 4 }), '1111 1111');
assert.strictEqual(formatToBinary(10, { group: 2 }), '10 10');
assert.strictEqual(formatToBinary(5, { group: 4 }), '101'); // no padding on the left
assert.strictEqual(formatToBinary(1023, { group: 4 }), '11 1111 1111');
});

it('applies grouping with negative numbers', () => {
assert.strictEqual(formatToBinary(-255, { group: 4 }), '-1111 1111');
});

it('handles large integers (MAX_SAFE_INTEGER)', () => {
const expected = Number.MAX_SAFE_INTEGER.toString(2);
assert.strictEqual(formatToBinary(Number.MAX_SAFE_INTEGER), expected);
});

it('throws TypeError for invalid inputs (type/NaN)', () => {
assert.throws(() => formatToBinary('5' as any), /Input must be a number/);
assert.throws(() => formatToBinary(null as any), /Input must be a number/);
assert.throws(() => formatToBinary(undefined as any), /Input must be a number/);
assert.throws(() => formatToBinary(NaN as any), /Input must be a number/);
});

it('throws TypeError for non-integer numbers', () => {
assert.throws(() => formatToBinary(3.14 as any), /Input must be an integer/);
assert.throws(() => formatToBinary(-2.5 as any), /Input must be an integer/);
});

it('throws TypeError for invalid group size', () => {
assert.throws(() => formatToBinary(10, { group: 0 }), /Group size must be a positive integer/);
assert.throws(() => formatToBinary(10, { group: -1 }), /Group size must be a positive integer/);
assert.throws(() => formatToBinary(10, { group: 2.5 as any }), /Group size must be a positive integer/);
assert.throws(() => formatToBinary(10, { group: '4' as any }), /Group size must be a positive integer/);
});
});