@@ -49,14 +49,19 @@ export class Decimal {
4949 throw new Error ( "Got more fractional digits than supported" ) ;
5050 }
5151
52- const quantity = `${ whole } ${ fractional . padEnd ( fractionalDigits , "0" ) } ` ;
52+ const quantity = BigInt ( `${ whole } ${ fractional . padEnd ( fractionalDigits , "0" ) } ` ) ;
5353
5454 return new Decimal ( quantity , fractionalDigits ) ;
5555 }
5656
5757 public static fromAtomics ( atomics : string , fractionalDigits : number ) : Decimal {
5858 Decimal . verifyFractionalDigits ( fractionalDigits ) ;
59- return new Decimal ( atomics , fractionalDigits ) ;
59+ if ( ! atomics . match ( / ^ [ 0 - 9 ] + $ / ) ) {
60+ throw new Error (
61+ "Invalid string format. Only non-negative integers in decimal representation supported." ,
62+ ) ;
63+ }
64+ return new Decimal ( BigInt ( atomics ) , fractionalDigits ) ;
6065 }
6166
6267 /**
@@ -67,7 +72,7 @@ export class Decimal {
6772 */
6873 public static zero ( fractionalDigits : number ) : Decimal {
6974 Decimal . verifyFractionalDigits ( fractionalDigits ) ;
70- return new Decimal ( "0" , fractionalDigits ) ;
75+ return new Decimal ( 0n , fractionalDigits ) ;
7176 }
7277
7378 /**
@@ -78,7 +83,7 @@ export class Decimal {
7883 */
7984 public static one ( fractionalDigits : number ) : Decimal {
8085 Decimal . verifyFractionalDigits ( fractionalDigits ) ;
81- return new Decimal ( "1" + "0" . repeat ( fractionalDigits ) , fractionalDigits ) ;
86+ return new Decimal ( 10n ** BigInt ( fractionalDigits ) , fractionalDigits ) ;
8287 }
8388
8489 private static verifyFractionalDigits ( fractionalDigits : number ) : void {
@@ -110,22 +115,16 @@ export class Decimal {
110115 readonly fractionalDigits : number ;
111116 } ;
112117
113- private constructor ( atomics : string , fractionalDigits : number ) {
114- if ( ! atomics . match ( / ^ [ 0 - 9 ] + $ / ) ) {
115- throw new Error (
116- "Invalid string format. Only non-negative integers in decimal representation supported." ,
117- ) ;
118- }
119-
118+ private constructor ( atomics : bigint , fractionalDigits : number ) {
120119 this . data = {
121- atomics : BigInt ( atomics ) ,
120+ atomics : atomics ,
122121 fractionalDigits : fractionalDigits ,
123122 } ;
124123 }
125124
126125 /** Creates a new instance with the same value */
127126 private clone ( ) : Decimal {
128- return new Decimal ( this . atomics , this . fractionalDigits ) ;
127+ return new Decimal ( this . data . atomics , this . data . fractionalDigits ) ;
129128 }
130129
131130 /** Returns the greatest decimal <= this which has no fractional part (rounding down) */
@@ -137,7 +136,7 @@ export class Decimal {
137136 if ( fractional === 0n ) {
138137 return this . clone ( ) ;
139138 } else {
140- return Decimal . fromAtomics ( ( whole * factor ) . toString ( ) , this . fractionalDigits ) ;
139+ return new Decimal ( whole * factor , this . fractionalDigits ) ;
141140 }
142141 }
143142
@@ -150,7 +149,31 @@ export class Decimal {
150149 if ( fractional === 0n ) {
151150 return this . clone ( ) ;
152151 } else {
153- return Decimal . fromAtomics ( ( ( whole + 1n ) * factor ) . toString ( ) , this . fractionalDigits ) ;
152+ return new Decimal ( ( whole + 1n ) * factor , this . fractionalDigits ) ;
153+ }
154+ }
155+
156+ /**
157+ * Creates a new Decimal with the same value using the new fractional digits.
158+ * Roughly speaking this can expand an 3.24 to 3.24000 or shrink a 5.4321 to 5.4.
159+ *
160+ * This allows you to perform arithmetic operations given two decimals
161+ * with different fractional digits by normalizing them.
162+ *
163+ * When new fractional digis is smaller than the original value, the amount
164+ * is truncated (not rounded!).
165+ */
166+ public adjustFractionalDigits ( newFractionalDigits : number ) : Decimal {
167+ Decimal . verifyFractionalDigits ( newFractionalDigits ) ;
168+ const diff = newFractionalDigits - this . fractionalDigits ;
169+ if ( diff > 0 ) {
170+ // expand
171+ return new Decimal ( this . data . atomics * 10n ** BigInt ( diff ) , newFractionalDigits ) ;
172+ } else if ( diff === 0 ) {
173+ return this . clone ( ) ;
174+ } else {
175+ // shrink
176+ return new Decimal ( this . data . atomics / 10n ** BigInt ( - diff ) , newFractionalDigits ) ;
154177 }
155178 }
156179
@@ -186,7 +209,7 @@ export class Decimal {
186209 public plus ( b : Decimal ) : Decimal {
187210 if ( this . fractionalDigits !== b . fractionalDigits ) throw new Error ( "Fractional digits do not match" ) ;
188211 const sum = this . data . atomics + b . data . atomics ;
189- return new Decimal ( sum . toString ( ) , this . fractionalDigits ) ;
212+ return new Decimal ( sum , this . fractionalDigits ) ;
190213 }
191214
192215 /**
@@ -199,7 +222,7 @@ export class Decimal {
199222 if ( this . fractionalDigits !== b . fractionalDigits ) throw new Error ( "Fractional digits do not match" ) ;
200223 const difference = this . data . atomics - b . data . atomics ;
201224 if ( difference < 0n ) throw new Error ( "Difference must not be negative" ) ;
202- return new Decimal ( difference . toString ( ) , this . fractionalDigits ) ;
225+ return new Decimal ( difference , this . fractionalDigits ) ;
203226 }
204227
205228 /**
@@ -209,7 +232,7 @@ export class Decimal {
209232 */
210233 public multiply ( b : Uint32 | Uint53 | Uint64 ) : Decimal {
211234 const product = this . data . atomics * b . toBigInt ( ) ;
212- return new Decimal ( product . toString ( ) , this . fractionalDigits ) ;
235+ return new Decimal ( product , this . fractionalDigits ) ;
213236 }
214237
215238 public equals ( b : Decimal ) : boolean {
0 commit comments