@@ -10,9 +10,10 @@ export type PrecisionNumberValue = Decimal.Value | PrecisionNumber | { toString:
1010 *
1111 * `PrecisionNumber` stores values as `Decimal`, rounds every mutating operation to
1212 * the configured decimal places, and exposes both mutable and immutable arithmetic
13- * methods. Mutable methods (`plus`, `minus`, `times`, `dividedBy`, `negate`,
14- * `absoluteValue`) update the current instance and return `this`; immutable methods
15- * prefixed with `to` return a new `PrecisionNumber` with the same precision settings.
13+ * methods. Mutable methods (`plus`, `minus`, `times`, `dividedBy`, `modulo`,
14+ * `pow`, `clamp`, `ceil`, `floor`, `negate`, `absoluteValue`) update the current
15+ * instance and return `this`; immutable methods prefixed with `to` return a new
16+ * `PrecisionNumber` with the same precision settings.
1617 *
1718 * @example
1819 * ```typescript
@@ -70,8 +71,26 @@ export class PrecisionNumber {
7071 return decimal . toDecimalPlaces ( this . #decimalPlaces, this . #rounding) ;
7172 }
7273
74+ #valueToString( value : PrecisionNumberValue ) {
75+ return value . toString ( ) . trim ( ) ;
76+ }
77+
7378 // Public getters
7479
80+ /**
81+ * Decimal places retained by mutating operations and default formatting.
82+ */
83+ get decimalPlaces ( ) {
84+ return this . #decimalPlaces;
85+ }
86+
87+ /**
88+ * Decimal.js rounding mode used by mutating operations and default formatting.
89+ */
90+ get rounding ( ) {
91+ return this . #rounding;
92+ }
93+
7594 /**
7695 * Fixed-decimal string using the instance precision and rounding mode.
7796 */
@@ -108,6 +127,43 @@ export class PrecisionNumber {
108127 return this ;
109128 }
110129
130+ /**
131+ * Rounds the current value up to the nearest integer in place.
132+ *
133+ * The stored value still uses this instance's configured decimal places for output.
134+ *
135+ * @returns {this } The current instance for chaining
136+ */
137+ ceil ( ) {
138+ this . #decimal = this . #decimalToFixedDecimal( this . #decimal. ceil ( ) ) ;
139+ return this ;
140+ }
141+
142+ /**
143+ * Restricts the current value to the inclusive range `[min, max]` in place.
144+ *
145+ * @param {PrecisionNumberValue } min - Lower bound
146+ * @param {PrecisionNumberValue } max - Upper bound
147+ *
148+ * @returns {this } The current instance for chaining
149+ *
150+ * @throws {Error } If `min` is greater than `max`
151+ */
152+ clamp ( min : PrecisionNumberValue , max : PrecisionNumberValue ) {
153+ const minDecimal = new Decimal ( this . #valueToString( min ) ) ;
154+ const maxDecimal = new Decimal ( this . #valueToString( max ) ) ;
155+ if ( minDecimal . gt ( maxDecimal ) ) throw new Error ( 'Invalid clamp range: min cannot be greater than max' ) ;
156+ this . #decimal = this . #decimalToFixedDecimal( Decimal . min ( Decimal . max ( this . #decimal, minDecimal ) , maxDecimal ) ) ;
157+ return this ;
158+ }
159+
160+ /**
161+ * Returns a new instance with the same value, decimal places, and rounding mode.
162+ */
163+ clone ( ) {
164+ return new PrecisionNumber ( this . #decimal, this . #decimalPlaces, this . #rounding) ;
165+ }
166+
111167 /**
112168 * Divides the current value by another value in place.
113169 *
@@ -116,7 +172,7 @@ export class PrecisionNumber {
116172 * @returns {this } The current instance for chaining
117173 */
118174 dividedBy ( value : PrecisionNumberValue ) {
119- this . #decimal = this . #decimalToFixedDecimal( this . #decimal. dividedBy ( value . toString ( ) . trim ( ) ) ) ;
175+ this . #decimal = this . #decimalToFixedDecimal( this . #decimal. dividedBy ( this . #valueToString ( value ) ) ) ;
120176 return this ;
121177 }
122178
@@ -128,21 +184,33 @@ export class PrecisionNumber {
128184 * @returns {boolean } `true` when both numeric values are equal
129185 */
130186 equals ( value : PrecisionNumberValue ) {
131- return this . #decimal. equals ( value . toString ( ) . trim ( ) ) ;
187+ return this . #decimal. equals ( this . #valueToString( value ) ) ;
188+ }
189+
190+ /**
191+ * Rounds the current value down to the nearest integer in place.
192+ *
193+ * The stored value still uses this instance's configured decimal places for output.
194+ *
195+ * @returns {this } The current instance for chaining
196+ */
197+ floor ( ) {
198+ this . #decimal = this . #decimalToFixedDecimal( this . #decimal. floor ( ) ) ;
199+ return this ;
132200 }
133201
134202 /**
135203 * Checks whether the current value is greater than another value.
136204 */
137205 gt ( value : PrecisionNumberValue ) {
138- return this . #decimal. gt ( value . toString ( ) . trim ( ) ) ;
206+ return this . #decimal. gt ( this . #valueToString ( value ) ) ;
139207 }
140208
141209 /**
142210 * Checks whether the current value is greater than or equal to another value.
143211 */
144212 gte ( value : PrecisionNumberValue ) {
145- return this . #decimal. gte ( value . toString ( ) . trim ( ) ) ;
213+ return this . #decimal. gte ( this . #valueToString ( value ) ) ;
146214 }
147215
148216 /**
@@ -191,21 +259,33 @@ export class PrecisionNumber {
191259 * Checks whether the current value is less than another value.
192260 */
193261 lt ( value : PrecisionNumberValue ) {
194- return this . #decimal. lt ( value . toString ( ) . trim ( ) ) ;
262+ return this . #decimal. lt ( this . #valueToString ( value ) ) ;
195263 }
196264
197265 /**
198266 * Checks whether the current value is less than or equal to another value.
199267 */
200268 lte ( value : PrecisionNumberValue ) {
201- return this . #decimal. lte ( value . toString ( ) . trim ( ) ) ;
269+ return this . #decimal. lte ( this . #valueToString ( value ) ) ;
202270 }
203271
204272 /**
205273 * Subtracts another value from the current value in place.
206274 */
207275 minus ( value : PrecisionNumberValue ) {
208- this . #decimal = this . #decimalToFixedDecimal( this . #decimal. minus ( value . toString ( ) . trim ( ) ) ) ;
276+ this . #decimal = this . #decimalToFixedDecimal( this . #decimal. minus ( this . #valueToString( value ) ) ) ;
277+ return this ;
278+ }
279+
280+ /**
281+ * Replaces the current value with the remainder after division by another value.
282+ *
283+ * @param {PrecisionNumberValue } value - Divisor used to compute the remainder
284+ *
285+ * @returns {this } The current instance for chaining
286+ */
287+ modulo ( value : PrecisionNumberValue ) {
288+ this . #decimal = this . #decimalToFixedDecimal( this . #decimal. modulo ( this . #valueToString( value ) ) ) ;
209289 return this ;
210290 }
211291
@@ -221,15 +301,27 @@ export class PrecisionNumber {
221301 * Adds another value to the current value in place.
222302 */
223303 plus ( value : PrecisionNumberValue ) {
224- this . #decimal = this . #decimalToFixedDecimal( this . #decimal. plus ( value . toString ( ) . trim ( ) ) ) ;
304+ this . #decimal = this . #decimalToFixedDecimal( this . #decimal. plus ( this . #valueToString( value ) ) ) ;
305+ return this ;
306+ }
307+
308+ /**
309+ * Raises the current value to an exponent in place.
310+ *
311+ * @param {PrecisionNumberValue } value - Exponent
312+ *
313+ * @returns {this } The current instance for chaining
314+ */
315+ pow ( value : PrecisionNumberValue ) {
316+ this . #decimal = this . #decimalToFixedDecimal( this . #decimal. pow ( this . #valueToString( value ) ) ) ;
225317 return this ;
226318 }
227319
228320 /**
229321 * Multiplies the current value by another value in place.
230322 */
231323 times ( value : PrecisionNumberValue ) {
232- this . #decimal = this . #decimalToFixedDecimal( this . #decimal. times ( value . toString ( ) . trim ( ) ) ) ;
324+ this . #decimal = this . #decimalToFixedDecimal( this . #decimal. times ( this . #valueToString ( value ) ) ) ;
233325 return this ;
234326 }
235327
@@ -240,17 +332,43 @@ export class PrecisionNumber {
240332 return new PrecisionNumber ( this . #decimal. absoluteValue ( ) , this . #decimalPlaces, this . #rounding) ;
241333 }
242334
335+ /**
336+ * Returns a new instance rounded up to the nearest integer.
337+ */
338+ toCeil ( ) {
339+ return new PrecisionNumber ( this . #decimal. ceil ( ) , this . #decimalPlaces, this . #rounding) ;
340+ }
341+
342+ /**
343+ * Returns a new instance restricted to the inclusive range `[min, max]`.
344+ *
345+ * @param {PrecisionNumberValue } min - Lower bound
346+ * @param {PrecisionNumberValue } max - Upper bound
347+ *
348+ * @throws {Error } If `min` is greater than `max`
349+ */
350+ toClamped ( min : PrecisionNumberValue , max : PrecisionNumberValue ) {
351+ return this . clone ( ) . clamp ( min , max ) ;
352+ }
353+
243354 /**
244355 * Returns a new instance divided by another value.
245356 */
246357 toDividedBy ( value : PrecisionNumberValue ) {
247358 return new PrecisionNumber (
248- this . #decimal. dividedBy ( value . toString ( ) . trim ( ) ) ,
359+ this . #decimal. dividedBy ( this . #valueToString ( value ) ) ,
249360 this . #decimalPlaces,
250361 this . #rounding,
251362 ) ;
252363 }
253364
365+ /**
366+ * Returns a new instance rounded down to the nearest integer.
367+ */
368+ toFloor ( ) {
369+ return new PrecisionNumber ( this . #decimal. floor ( ) , this . #decimalPlaces, this . #rounding) ;
370+ }
371+
254372 /**
255373 * Serializes to the fixed-decimal string.
256374 */
@@ -262,7 +380,22 @@ export class PrecisionNumber {
262380 * Returns a new instance with another value subtracted.
263381 */
264382 toMinus ( value : PrecisionNumberValue ) {
265- return new PrecisionNumber ( this . #decimal. minus ( value . toString ( ) . trim ( ) ) , this . #decimalPlaces, this . #rounding) ;
383+ return new PrecisionNumber (
384+ this . #decimal. minus ( this . #valueToString( value ) ) ,
385+ this . #decimalPlaces,
386+ this . #rounding,
387+ ) ;
388+ }
389+
390+ /**
391+ * Returns a new instance with the remainder after division by another value.
392+ */
393+ toModulo ( value : PrecisionNumberValue ) {
394+ return new PrecisionNumber (
395+ this . #decimal. modulo ( this . #valueToString( value ) ) ,
396+ this . #decimalPlaces,
397+ this . #rounding,
398+ ) ;
266399 }
267400
268401 /**
@@ -276,7 +409,23 @@ export class PrecisionNumber {
276409 * Returns a new instance with another value added.
277410 */
278411 toPlus ( value : PrecisionNumberValue ) {
279- return new PrecisionNumber ( this . #decimal. plus ( value . toString ( ) . trim ( ) ) , this . #decimalPlaces, this . #rounding) ;
412+ return new PrecisionNumber ( this . #decimal. plus ( this . #valueToString( value ) ) , this . #decimalPlaces, this . #rounding) ;
413+ }
414+
415+ /**
416+ * Returns a new instance raised to an exponent.
417+ */
418+ toPow ( value : PrecisionNumberValue ) {
419+ return new PrecisionNumber ( this . #decimal. pow ( this . #valueToString( value ) ) , this . #decimalPlaces, this . #rounding) ;
420+ }
421+
422+ /**
423+ * Converts the current value to a JavaScript number.
424+ *
425+ * Use `toString`/`value` when preserving decimal precision is more important than native-number ergonomics.
426+ */
427+ toNumber ( ) {
428+ return this . #decimal. toNumber ( ) ;
280429 }
281430
282431 /**
@@ -297,6 +446,10 @@ export class PrecisionNumber {
297446 * Returns a new instance multiplied by another value.
298447 */
299448 toTimes ( value : PrecisionNumberValue ) {
300- return new PrecisionNumber ( this . #decimal. times ( value . toString ( ) . trim ( ) ) , this . #decimalPlaces, this . #rounding) ;
449+ return new PrecisionNumber (
450+ this . #decimal. times ( this . #valueToString( value ) ) ,
451+ this . #decimalPlaces,
452+ this . #rounding,
453+ ) ;
301454 }
302455}
0 commit comments