bigdecimal-string 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 ozJSey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,299 @@
1
+ # bigdecimal-string
2
+
3
+ [![npm version](https://img.shields.io/npm/v/bigdecimal-string.svg)](https://www.npmjs.com/package/bigdecimal-string)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Native-blue.svg)](https://www.typescriptlang.org/)
6
+
7
+ **Display really large numbers on screen without scientific notation.** Convert `1e15` to `"1,000,000,000,000,000.00"` - human readable, formatted, and precise.
8
+
9
+ **Written in TypeScript. Full type safety. No `@types` package needed.**
10
+
11
+ **GitHub:** [https://github.com/ozJSey/big_decimal_string](https://github.com/ozJSey/big_decimal_string)
12
+
13
+ ## The Problem
14
+
15
+ JavaScript displays large numbers in scientific notation, making them unreadable for users:
16
+
17
+ ```javascript
18
+ const bigNumber = 1000000000000000;
19
+ console.log(bigNumber); // 1e+15 - not user friendly!
20
+ console.log(bigNumber.toString()); // "1000000000000000" - no formatting
21
+
22
+ // Even worse with decimals
23
+ const price = 0.1 + 0.2;
24
+ console.log(price); // 0.30000000000000004 - wrong!
25
+ ```
26
+
27
+ ## The Solution
28
+
29
+ ```typescript
30
+ import { bd } from 'bigdecimal-string';
31
+
32
+ // Large numbers become readable
33
+ bd("1e15").toString(); // "1000000000000000.00"
34
+ bd("1e15").toFormat(); // "1,000,000,000,000,000.00" ✓
35
+
36
+ // Precise decimal arithmetic (no more 0.30000000000000004)
37
+ bd("0.1").add("0.2").toString(); // "0.30" ✓
38
+
39
+ // Perfect for displaying prices, quantities, financial data
40
+ bd("9876543210.99").toFormat(); // "9,876,543,210.99" ✓
41
+ ```
42
+
43
+ ## Features
44
+
45
+ - **Human-readable large numbers** - No more `1e15`, display `1,000,000,000,000,000.00`
46
+ - **Prettify with commas** - `toFormat()` adds thousand separators automatically
47
+ - **Precise decimals** - Solves the `0.1 + 0.2` problem using BigInt internally
48
+ - **Native TypeScript** - Written in TypeScript, full type inference, no `@types` needed
49
+ - **Chainable API** - Fluent method chaining for calculations
50
+ - **Zero dependencies** - Pure TypeScript, ~4KB minified
51
+ - **Immutable** - All operations return new instances
52
+
53
+ ## Installation
54
+
55
+ ```bash
56
+ npm install bigdecimal-string
57
+ ```
58
+
59
+ ## Usage Examples
60
+
61
+ ### Displaying Large Numbers
62
+
63
+ The primary use case - converting scientific notation or large numbers into displayable strings:
64
+
65
+ ```typescript
66
+ import { bd } from 'bigdecimal-string';
67
+
68
+ // API returns scientific notation? No problem.
69
+ const apiValue = "2.5e12"; // From server
70
+ bd(apiValue).toFormat(); // "2,500,000,000,000.00"
71
+
72
+ // Large inventory counts
73
+ bd("1000000000").toFormat(); // "1,000,000,000.00"
74
+
75
+ // Without prettify (raw string)
76
+ bd("1e9").toString(); // "1000000000.00"
77
+ ```
78
+
79
+ ### E-Commerce / Financial Display
80
+
81
+ ```typescript
82
+ // Product prices
83
+ const price = bd("1299.99");
84
+ const quantity = bd(1000000);
85
+ const total = price.multiply(quantity);
86
+
87
+ console.log(`Total: $${total.toFormat()}`); // "Total: $1,299,990,000.00"
88
+
89
+ // Tax calculations (use precision 4 for rates like 8.25%, then round to 2 for currency)
90
+ const subtotal = bd("999.99");
91
+ const taxRate = bd("0.0825", 4); // 8.25% - specify precision for small decimals
92
+ const tax = subtotal.multiply(taxRate).setScale(2); // Round to 2 decimals
93
+ const grandTotal = subtotal.add(tax);
94
+
95
+ console.log(`Tax: $${tax.toFormat()}`); // "Tax: $82.50"
96
+ console.log(`Total: $${grandTotal.toFormat()}`); // "Total: $1,082.49"
97
+ ```
98
+
99
+ ### Dashboard / Analytics
100
+
101
+ ```typescript
102
+ // User counts, revenue, metrics
103
+ const dailyActiveUsers = bd("12345678");
104
+ const revenue = bd("9876543210.50");
105
+ const avgSessionTime = bd("245.7");
106
+
107
+ console.log(`DAU: ${dailyActiveUsers.toFormat()}`); // "DAU: 12,345,678.00"
108
+ console.log(`Revenue: $${revenue.toFormat()}`); // "Revenue: $9,876,543,210.50"
109
+ ```
110
+
111
+ ### Precise Calculations (Bonus Feature)
112
+
113
+ While display formatting is the main purpose, you also get precise decimal math:
114
+
115
+ ```typescript
116
+ // The classic floating-point problem - SOLVED
117
+ bd("0.1").add("0.2").eq("0.3"); // true ✓ (JS would give false)
118
+
119
+ // Currency calculations without rounding errors
120
+ bd("19.99")
121
+ .multiply(3)
122
+ .subtract("5.00")
123
+ .add("2.50")
124
+ .toString(); // "57.47" (precise)
125
+ ```
126
+
127
+ ## API Reference
128
+
129
+ ### Creating Instances
130
+
131
+ ```typescript
132
+ // From string (recommended)
133
+ bd("123.45")
134
+ bd("1e15") // Scientific notation OK
135
+ bd("1,234.56") // Commas are stripped
136
+
137
+ // From number
138
+ bd(123.45)
139
+ bd(1000000000000000)
140
+
141
+ // With custom precision
142
+ bd("123.456", 3) // 3 decimal places
143
+ bd("100", 4) // "100.0000"
144
+ ```
145
+
146
+ ### Formatting Methods
147
+
148
+ ```typescript
149
+ const value = bd("1234567.89");
150
+
151
+ // Basic string (no formatting)
152
+ value.toString(); // "1234567.89"
153
+
154
+ // With thousand separators
155
+ value.toString({ prettify: true }); // "1,234,567.89"
156
+
157
+ // Shorthand for prettify
158
+ value.toFormat(); // "1,234,567.89"
159
+
160
+ // Fixed decimal places + prettify
161
+ value.toFixed(4); // "1234567.8900"
162
+ value.toFixed(2, { prettify: true }); // "1,234,567.89"
163
+ ```
164
+
165
+ ### Arithmetic (Chainable)
166
+
167
+ ```typescript
168
+ bd("100.00").add("50.00"); // "150.00"
169
+ bd("100.00").subtract("30.00"); // "70.00"
170
+ bd("100.00").multiply(2); // "200.00"
171
+ bd("100.00").divide(3); // "33.33"
172
+ bd("10.00").mod(3); // "1.00"
173
+
174
+ // Chaining
175
+ bd("1000")
176
+ .subtract("100")
177
+ .multiply("1.5")
178
+ .divide(2)
179
+ .add("50")
180
+ .toFormat(); // "725.00"
181
+ ```
182
+
183
+ ### Comparisons
184
+
185
+ ```typescript
186
+ bd("10").gt("5"); // true (greater than)
187
+ bd("10").gte("10"); // true (greater than or equal)
188
+ bd("5").lt("10"); // true (less than)
189
+ bd("5").lte("5"); // true (less than or equal)
190
+ bd("10").eq("10"); // true (equals)
191
+ ```
192
+
193
+ ### Utility Methods
194
+
195
+ ```typescript
196
+ bd("-50").abs(); // "50.00"
197
+ bd("50").negate(); // "-50.00"
198
+ bd("0").isZero(); // true
199
+ bd("10").isPositive(); // true
200
+ bd("-10").isNegative(); // true
201
+ ```
202
+
203
+ ### Static Methods
204
+
205
+ ```typescript
206
+ BigDecimal.sum("10", "20", "30"); // "60.00"
207
+ BigDecimal.max("5", "10", "3"); // "10.00"
208
+ BigDecimal.min("5", "10", "3"); // "3.00"
209
+ BigDecimal.zero(); // "0.00"
210
+ BigDecimal.one(); // "1.00"
211
+ ```
212
+
213
+ ## Real-World Examples
214
+
215
+ ### Cryptocurrency Display
216
+
217
+ ```typescript
218
+ // Crypto amounts often come in scientific notation
219
+ const btcBalance = bd("0.00000001"); // 1 satoshi
220
+ const ethBalance = bd("1.5e18"); // Wei to display
221
+
222
+ console.log(`BTC: ${btcBalance.toString()}`); // "0.00000001"
223
+ console.log(`Wei: ${ethBalance.toFormat()}`); // "1,500,000,000,000,000,000.00"
224
+ ```
225
+
226
+ ### Stock Market Data
227
+
228
+ ```typescript
229
+ const marketCap = bd("2.5e12"); // $2.5 trillion
230
+ const volume = bd("987654321");
231
+ const priceChange = bd("-2.34"); // Already as percentage
232
+
233
+ console.log(`Market Cap: $${marketCap.toFormat()}`); // "$2,500,000,000,000.00"
234
+ console.log(`Volume: ${volume.toFormat()}`); // "987,654,321.00"
235
+ console.log(`Change: ${priceChange.toString()}%`); // "-2.34%"
236
+ ```
237
+
238
+ ### Shopping Cart
239
+
240
+ ```typescript
241
+ const items = [
242
+ { name: "Laptop", price: "1299.99", qty: 2 },
243
+ { name: "Mouse", price: "49.99", qty: 3 },
244
+ { name: "Keyboard", price: "149.99", qty: 1 },
245
+ ];
246
+
247
+ const subtotal = BigDecimal.sum(
248
+ ...items.map(item => bd(item.price).multiply(item.qty))
249
+ );
250
+ const tax = subtotal.multiply("0.08"); // 8% tax
251
+ const total = subtotal.add(tax);
252
+
253
+ console.log(`Subtotal: $${subtotal.toFormat()}`); // "$2,899.94"
254
+ console.log(`Tax: $${tax.toFormat()}`); // "$232.00"
255
+ console.log(`Total: $${total.toFormat()}`); // "$3,131.94"
256
+ ```
257
+
258
+ ## TypeScript Native
259
+
260
+ Unlike alternatives that require separate `@types` packages or have incomplete type definitions, **bigdecimal-string is written in TypeScript from the ground up**.
261
+
262
+ ```typescript
263
+ import { BigDecimal, bd, BigDecimalInput, RoundingMode } from 'bigdecimal-string';
264
+
265
+ // Full type inference - no 'any' types
266
+ function formatPrice(amount: BigDecimalInput): string {
267
+ return bd(amount).toFormat();
268
+ }
269
+
270
+ // Generics and type safety work out of the box
271
+ const prices: BigDecimal[] = [bd("10.00"), bd("20.00")];
272
+ const total = BigDecimal.sum(...prices); // TypeScript knows this is BigDecimal
273
+
274
+ // RoundingMode enum is properly typed
275
+ const rounded = bd("10.555").setScale(2, RoundingMode.HALF_UP);
276
+ ```
277
+
278
+ **Comparison with alternatives:**
279
+
280
+ | Library | TypeScript | Types Source |
281
+ |---------|-----------|--------------|
282
+ | bigdecimal-string | Native | Built-in ✓ |
283
+ | big.js | JS | @types/big.js |
284
+ | decimal.js | JS | @types/decimal.js |
285
+ | bignumber.js | JS | @types/bignumber.js |
286
+
287
+ ## Why Not Just Use `toLocaleString()`?
288
+
289
+ ```javascript
290
+ // toLocaleString fails with large numbers
291
+ (1e21).toLocaleString(); // "1e+21" - still scientific!
292
+
293
+ // Our solution works
294
+ bd("1e21").toFormat(); // "1,000,000,000,000,000,000,000.00" ✓
295
+ ```
296
+
297
+ ## License
298
+
299
+ MIT
@@ -0,0 +1,289 @@
1
+ /**
2
+ * BigDecimal - Precise decimal arithmetic for JavaScript
3
+ *
4
+ * Avoids floating-point precision issues by using bigint internally.
5
+ * Supports chainable operations and configurable precision.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const price = new BigDecimal("19.99");
10
+ * const tax = price.multiply(0.08);
11
+ * const total = price.add(tax);
12
+ *
13
+ * // Chaining
14
+ * const result = new BigDecimal("100.00")
15
+ * .subtract("25.50")
16
+ * .multiply(2)
17
+ * .add("10")
18
+ * .toString(); // "159.00"
19
+ * ```
20
+ */
21
+ type BigDecimalInput = string | number | bigint | BigDecimal | null | undefined;
22
+ /**
23
+ * Configuration options for BigDecimal operations
24
+ */
25
+ interface BigDecimalConfig {
26
+ /** Number of decimal places (default: 2) */
27
+ precision?: number;
28
+ /** Rounding mode for division and precision changes */
29
+ roundingMode?: RoundingMode;
30
+ }
31
+ declare enum RoundingMode {
32
+ /** Round towards positive infinity */
33
+ CEILING = "CEILING",
34
+ /** Round towards negative infinity */
35
+ FLOOR = "FLOOR",
36
+ /** Round towards zero (truncate) */
37
+ DOWN = "DOWN",
38
+ /** Round away from zero */
39
+ UP = "UP",
40
+ /** Round to nearest, ties go to even (banker's rounding) */
41
+ HALF_EVEN = "HALF_EVEN",
42
+ /** Round to nearest, ties round up */
43
+ HALF_UP = "HALF_UP",
44
+ /** Round to nearest, ties round down */
45
+ HALF_DOWN = "HALF_DOWN"
46
+ }
47
+ declare class BigDecimal {
48
+ /** Internal representation: value * 10^scale */
49
+ private readonly unscaledValue;
50
+ /** Number of decimal places */
51
+ private readonly scale;
52
+ /**
53
+ * Creates a new BigDecimal instance
54
+ * @param value - The value to create from (string, number, bigint, or another BigDecimal)
55
+ * @param precision - Number of decimal places (default: auto-detected or 2)
56
+ */
57
+ constructor(value?: BigDecimalInput, precision?: number);
58
+ /**
59
+ * Parse a value into unscaled bigint and scale
60
+ */
61
+ private static parse;
62
+ /**
63
+ * Convert scientific notation to plain decimal string
64
+ */
65
+ private static scientificToPlain;
66
+ /**
67
+ * Calculate 10^n as bigint
68
+ */
69
+ private static powerOf10;
70
+ /**
71
+ * Add another value to this BigDecimal
72
+ * @returns A new BigDecimal with the result
73
+ */
74
+ add(other: BigDecimalInput): BigDecimal;
75
+ /**
76
+ * Subtract another value from this BigDecimal
77
+ * @returns A new BigDecimal with the result
78
+ */
79
+ subtract(other: BigDecimalInput): BigDecimal;
80
+ /**
81
+ * Alias for subtract
82
+ */
83
+ minus(other: BigDecimalInput): BigDecimal;
84
+ /**
85
+ * Alias for add
86
+ */
87
+ plus(other: BigDecimalInput): BigDecimal;
88
+ /**
89
+ * Multiply this BigDecimal by another value
90
+ * @returns A new BigDecimal with the result
91
+ */
92
+ multiply(other: BigDecimalInput): BigDecimal;
93
+ /**
94
+ * Alias for multiply
95
+ */
96
+ times(other: BigDecimalInput): BigDecimal;
97
+ /**
98
+ * Divide this BigDecimal by another value
99
+ * @param other - The divisor
100
+ * @param precision - Precision for the result (default: this.scale)
101
+ * @param roundingMode - How to round (default: HALF_UP)
102
+ * @returns A new BigDecimal with the result
103
+ */
104
+ divide(other: BigDecimalInput, precision?: number, roundingMode?: RoundingMode): BigDecimal;
105
+ /**
106
+ * Alias for divide
107
+ */
108
+ dividedBy(other: BigDecimalInput, precision?: number, roundingMode?: RoundingMode): BigDecimal;
109
+ /**
110
+ * Get the remainder of division
111
+ */
112
+ mod(other: BigDecimalInput): BigDecimal;
113
+ /**
114
+ * Compare this BigDecimal to another
115
+ * @returns -1 if this < other, 0 if equal, 1 if this > other
116
+ */
117
+ compareTo(other: BigDecimalInput): -1 | 0 | 1;
118
+ /**
119
+ * Check if this BigDecimal equals another value
120
+ */
121
+ equals(other: BigDecimalInput): boolean;
122
+ /**
123
+ * Alias for equals
124
+ */
125
+ eq(other: BigDecimalInput): boolean;
126
+ /**
127
+ * Check if this BigDecimal is less than another
128
+ */
129
+ lessThan(other: BigDecimalInput): boolean;
130
+ /**
131
+ * Alias for lessThan
132
+ */
133
+ lt(other: BigDecimalInput): boolean;
134
+ /**
135
+ * Check if this BigDecimal is less than or equal to another
136
+ */
137
+ lessThanOrEqual(other: BigDecimalInput): boolean;
138
+ /**
139
+ * Alias for lessThanOrEqual
140
+ */
141
+ lte(other: BigDecimalInput): boolean;
142
+ /**
143
+ * Check if this BigDecimal is greater than another
144
+ */
145
+ greaterThan(other: BigDecimalInput): boolean;
146
+ /**
147
+ * Alias for greaterThan
148
+ */
149
+ gt(other: BigDecimalInput): boolean;
150
+ /**
151
+ * Check if this BigDecimal is greater than or equal to another
152
+ */
153
+ greaterThanOrEqual(other: BigDecimalInput): boolean;
154
+ /**
155
+ * Alias for greaterThanOrEqual
156
+ */
157
+ gte(other: BigDecimalInput): boolean;
158
+ /**
159
+ * Check if this BigDecimal is zero
160
+ */
161
+ isZero(): boolean;
162
+ /**
163
+ * Check if this BigDecimal is positive (> 0)
164
+ */
165
+ isPositive(): boolean;
166
+ /**
167
+ * Check if this BigDecimal is negative (< 0)
168
+ */
169
+ isNegative(): boolean;
170
+ /**
171
+ * Get the absolute value
172
+ * @returns A new BigDecimal with the absolute value
173
+ */
174
+ abs(): BigDecimal;
175
+ /**
176
+ * Negate this BigDecimal
177
+ * @returns A new BigDecimal with the opposite sign
178
+ */
179
+ negate(): BigDecimal;
180
+ /**
181
+ * Get the sign of this BigDecimal
182
+ * @returns -1, 0, or 1
183
+ */
184
+ sign(): -1 | 0 | 1;
185
+ /**
186
+ * Change the scale (number of decimal places)
187
+ */
188
+ setScale(newScale: number, roundingMode?: RoundingMode): BigDecimal;
189
+ /**
190
+ * Get the precision (number of decimal places)
191
+ */
192
+ getPrecision(): number;
193
+ /**
194
+ * Convert to string representation
195
+ * @param options - Formatting options
196
+ * @param options.prettify - Add thousand separators (commas)
197
+ *
198
+ * @example
199
+ * ```ts
200
+ * bd("1234567.89").toString() // "1234567.89"
201
+ * bd("1234567.89").toString({ prettify: true }) // "1,234,567.89"
202
+ * bd("1e15").toString() // "1000000000000000.00"
203
+ * bd("1e15").toString({ prettify: true }) // "1,000,000,000,000,000.00"
204
+ * ```
205
+ */
206
+ toString(options?: {
207
+ prettify?: boolean;
208
+ }): string;
209
+ /**
210
+ * Format as a display string with thousand separators
211
+ * Shorthand for toString({ prettify: true })
212
+ *
213
+ * @example
214
+ * ```ts
215
+ * bd("1234567.89").toFormat() // "1,234,567.89"
216
+ * bd("1e12").toFormat() // "1,000,000,000,000.00"
217
+ * ```
218
+ */
219
+ toFormat(): string;
220
+ /**
221
+ * Add thousand separators to an integer string
222
+ */
223
+ private static addThousandSeparators;
224
+ /**
225
+ * Convert to number (may lose precision for large values)
226
+ * @warning Use with caution - JavaScript numbers have limited precision
227
+ */
228
+ toNumber(): number;
229
+ /**
230
+ * Format with fixed decimal places
231
+ * @param decimals - Number of decimal places
232
+ * @param options - Formatting options
233
+ */
234
+ toFixed(decimals: number, options?: {
235
+ prettify?: boolean;
236
+ }): string;
237
+ /**
238
+ * Get the integer part only
239
+ */
240
+ toInteger(): bigint;
241
+ /**
242
+ * valueOf for implicit conversions
243
+ */
244
+ valueOf(): number;
245
+ /**
246
+ * Create from unscaled value and scale
247
+ */
248
+ private static fromUnscaled;
249
+ /**
250
+ * Create a BigDecimal with value zero
251
+ */
252
+ static zero(precision?: number): BigDecimal;
253
+ /**
254
+ * Create a BigDecimal with value one
255
+ */
256
+ static one(precision?: number): BigDecimal;
257
+ /**
258
+ * Sum multiple values
259
+ */
260
+ static sum(...values: BigDecimalInput[]): BigDecimal;
261
+ /**
262
+ * Get the maximum value
263
+ */
264
+ static max(...values: BigDecimalInput[]): BigDecimal;
265
+ /**
266
+ * Get the minimum value
267
+ */
268
+ static min(...values: BigDecimalInput[]): BigDecimal;
269
+ /**
270
+ * Align two BigDecimals to the same scale
271
+ */
272
+ private static alignScales;
273
+ /**
274
+ * Perform division with rounding
275
+ */
276
+ private static roundDivision;
277
+ }
278
+ /**
279
+ * Shorthand factory function
280
+ *
281
+ * @example
282
+ * ```ts
283
+ * const price = bd("19.99");
284
+ * const total = bd(100).subtract(25).multiply(2);
285
+ * ```
286
+ */
287
+ declare function bd(value?: BigDecimalInput, precision?: number): BigDecimal;
288
+
289
+ export { BigDecimal, type BigDecimalConfig, type BigDecimalInput, RoundingMode, bd, BigDecimal as default };