13
13
* @author Jesse Alama <[email protected] >
14
14
*/
15
15
16
- import { Digit , DigitOrTen } from "./common.mjs" ;
16
+ import { Digit , DigitOrTen , RoundingMode , ROUNDING_MODES } from "./common.mjs" ;
17
17
import { Rational } from "./rational.mjs" ;
18
18
19
19
const EXPONENT_MIN = - 6176 ;
@@ -32,26 +32,18 @@ function quantum(s: string): number {
32
32
return quantum ( s . substring ( 1 ) ) ;
33
33
}
34
34
35
- if ( s . match ( / ^ [ 0 - 9 ] + / ) ) {
35
+ if ( ! s . match ( / [ . ] / ) ) {
36
36
return 0 ;
37
37
}
38
38
39
- if ( s . match ( / ^ [ 0 - 9 ] + [ e E ] [ + - ] ? [ 0 - 9 ] + / ) ) {
40
- let exp = parseInt ( s . split ( / [ e E ] / ) [ 1 ] ) ;
41
- return 0 - exp ;
42
- }
43
-
44
- if ( s . match ( / [ . ] / ) ) {
45
- let [ lhs , rhs ] = s . split ( "." ) ;
46
- if ( s . match ( / [ e E ] / ) ) {
47
- let beforeExp = lhs . split ( / [ e E ] / ) [ 0 ] ;
48
- return beforeExp . length ;
49
- }
39
+ let [ _ , rhs ] = s . split ( "." ) ;
50
40
51
- return rhs . length ;
41
+ if ( rhs . match ( / [ e E ] / ) ) {
42
+ let [ dec , exp ] = rhs . split ( / [ e E ] / ) ;
43
+ return parseInt ( exp ) - dec . length ;
52
44
}
53
45
54
- throw new SyntaxError ( `Cannot determine quantum for " ${ s } "` ) ;
46
+ return 0 - rhs . length ;
55
47
}
56
48
57
49
type NaNValue = "NaN" ;
@@ -68,17 +60,18 @@ type Decimal128Value = NaNValue | InfiniteValue | FiniteValue;
68
60
const NAN = "NaN" ;
69
61
const POSITIVE_INFINITY = "Infinity" ;
70
62
const NEGATIVE_INFINITY = "-Infinity" ;
71
- const TEN_MAX_EXPONENT = new Rational ( bigTen , bigOne ) . scale10 (
72
- MAX_SIGNIFICANT_DIGITS
63
+ const TEN_MAX_EXPONENT = new Rational (
64
+ bigTen ** BigInt ( MAX_SIGNIFICANT_DIGITS ) ,
65
+ bigOne
73
66
) ;
74
67
75
68
function pickQuantum ( d : Rational , preferredQuantum : number ) : number {
76
69
return preferredQuantum ;
77
70
}
78
71
79
- function validateConstructorData ( x : Decimal128Value ) : void {
72
+ function validateConstructorData ( x : Decimal128Value ) : Decimal128Value {
80
73
if ( x === "NaN" || x === "Infinity" || x === "-Infinity" ) {
81
- return ; // no further validation needed
74
+ return x ; // no further validation needed
82
75
}
83
76
84
77
let val = x as FiniteValue ;
@@ -94,7 +87,7 @@ function validateConstructorData(x: Decimal128Value): void {
94
87
}
95
88
96
89
if ( v === "0" || v === "-0" ) {
97
- return ; // no further validation needed
90
+ return { cohort : v , quantum : q } ; // no further validation needed
98
91
}
99
92
100
93
let scaledV = v . scale10 ( 0 - q ) ;
@@ -112,10 +105,21 @@ function validateConstructorData(x: Decimal128Value): void {
112
105
}
113
106
114
107
if ( absV . cmp ( TEN_MAX_EXPONENT ) > 0 ) {
115
- throw new RangeError ( `Absolute value of scaled cohort is too big` ) ;
108
+ let s = v . toPrecision ( MAX_SIGNIFICANT_DIGITS + 1 ) ;
109
+ let numFractionalDigits = 0 ;
110
+ if ( s . match ( / [ . ] / ) ) {
111
+ let [ _ , rhs ] = s . split ( "." ) ;
112
+ numFractionalDigits = rhs . length ;
113
+ }
114
+ if ( numFractionalDigits === 0 ) {
115
+ throw new RangeError ( "Integer too large" ) ;
116
+ }
117
+ let newV = v . round ( numFractionalDigits - 1 , "halfEven" ) ;
118
+
119
+ return { cohort : newV , quantum : q } ;
116
120
}
117
121
118
- return ;
122
+ return { cohort : v , quantum : q } ;
119
123
}
120
124
121
125
function handleDecimalNotation ( s : string ) : Decimal128Value {
@@ -215,16 +219,6 @@ function roundIt(
215
219
}
216
220
}
217
221
218
- type RoundingMode = "ceil" | "floor" | "trunc" | "halfEven" | "halfExpand" ;
219
-
220
- const ROUNDING_MODES : RoundingMode [ ] = [
221
- "ceil" ,
222
- "floor" ,
223
- "trunc" ,
224
- "halfEven" ,
225
- "halfExpand" ,
226
- ] ;
227
-
228
222
export class Decimal128 {
229
223
private readonly cohort :
230
224
| "NaN"
@@ -255,7 +249,7 @@ export class Decimal128 {
255
249
256
250
let data = handleDecimalNotation ( s ) ;
257
251
258
- validateConstructorData ( data ) ;
252
+ data = validateConstructorData ( data ) ;
259
253
260
254
if ( data === "NaN" || data === "Infinity" || data === "-Infinity" ) {
261
255
this . cohort = data ;
@@ -348,7 +342,7 @@ export class Decimal128 {
348
342
349
343
public mantissa ( ) : Decimal128 {
350
344
let [ sig , _ ] = this . significandAndExponent ( ) ;
351
- return new Decimal128 ( sig . toDecimalPlaces ( MAX_SIGNIFICANT_DIGITS ) ) ;
345
+ return new Decimal128 ( sig . toFixed ( MAX_SIGNIFICANT_DIGITS ) ) ;
352
346
}
353
347
354
348
public scale10 ( n : number ) : Decimal128 {
@@ -403,13 +397,13 @@ export class Decimal128 {
403
397
404
398
private emitDecimal ( ) : string {
405
399
let v = this . cohort as Rational ;
406
- return v . toDecimalPlaces ( MAX_SIGNIFICANT_DIGITS ) ;
400
+ return v . toPrecision ( MAX_SIGNIFICANT_DIGITS ) ;
407
401
}
408
402
409
403
/**
410
404
* Returns a digit string representing this Decimal128.
411
405
*/
412
- toString ( ) : string {
406
+ toString ( opts ?: { format ?: "decimal" | "exponential" } ) : string {
413
407
if ( this . isNaN ( ) ) {
414
408
return NAN ;
415
409
}
@@ -419,8 +413,23 @@ export class Decimal128 {
419
413
}
420
414
421
415
let asDecimalString = this . emitDecimal ( ) ;
416
+ let format = undefined ;
422
417
423
- if ( asDecimalString . length > 20 ) {
418
+ if ( "object" === typeof opts && "string" === typeof opts . format ) {
419
+ if ( opts . format === "exponential" ) {
420
+ format = "exponential" ;
421
+ } else if ( opts . format === "decimal" ) {
422
+ format = "decimal" ;
423
+ } else {
424
+ throw new TypeError ( `Invalid toString format "${ opts . format } "` ) ;
425
+ }
426
+ }
427
+
428
+ if ( format === "exponential" ) {
429
+ return this . emitExponential ( ) ;
430
+ }
431
+
432
+ if ( format === undefined && asDecimalString . length > 20 ) {
424
433
return this . emitExponential ( ) ;
425
434
}
426
435
@@ -725,11 +734,11 @@ export class Decimal128 {
725
734
let ourQuantum = this . quantum as number ;
726
735
let theirQuantum = x . quantum as number ;
727
736
let sum = Rational . add ( ourCohort , theirCohort ) ;
728
- let prefferedQuantum = Math . min ( ourQuantum , theirQuantum ) ;
737
+ let preferredQuantum = Math . min ( ourQuantum , theirQuantum ) ;
729
738
730
739
return Decimal128 . fromCohortAndQuantum (
731
740
sum ,
732
- pickQuantum ( sum , prefferedQuantum )
741
+ pickQuantum ( sum , preferredQuantum )
733
742
) ;
734
743
}
735
744
@@ -776,11 +785,11 @@ export class Decimal128 {
776
785
let ourExponent = this . quantum as number ;
777
786
let theirExponent = x . quantum as number ;
778
787
let difference = Rational . subtract ( ourCohort , theirCohort ) ;
779
- let prefferedExponent = Math . min ( ourExponent , theirExponent ) ;
788
+ let preferredQuantum = Math . min ( ourExponent , theirExponent ) ;
780
789
781
790
return Decimal128 . fromCohortAndQuantum (
782
791
difference ,
783
- pickQuantum ( difference , prefferedExponent )
792
+ pickQuantum ( difference , preferredQuantum )
784
793
) ;
785
794
}
786
795
@@ -837,11 +846,11 @@ export class Decimal128 {
837
846
let ourExponent = this . quantum as number ;
838
847
let theirExponent = x . quantum as number ;
839
848
let product = Rational . multiply ( ourCohort , theirCohort ) ;
840
- let prefferedExponent = ourExponent + theirExponent ;
849
+ let preferredQuantum = ourExponent + theirExponent ;
841
850
842
851
return Decimal128 . fromCohortAndQuantum (
843
852
product ,
844
- pickQuantum ( product , prefferedExponent )
853
+ pickQuantum ( product , preferredQuantum )
845
854
) ;
846
855
}
847
856
@@ -951,52 +960,23 @@ export class Decimal128 {
951
960
numDecimalDigits : number = 0 ,
952
961
mode : RoundingMode = ROUNDING_MODE_DEFAULT
953
962
) : Decimal128 {
954
- if ( this . isNaN ( ) || ! this . isFinite ( ) ) {
955
- return this . clone ( ) ;
956
- }
957
-
958
963
if ( ! ROUNDING_MODES . includes ( mode ) ) {
959
964
throw new RangeError ( `Invalid rounding mode "${ mode } "` ) ;
960
965
}
961
966
962
- if ( numDecimalDigits < 0 ) {
963
- numDecimalDigits = 0 ;
967
+ if ( this . isNaN ( ) || ! this . isFinite ( ) ) {
968
+ return this . clone ( ) ;
964
969
}
965
970
966
- let s = this . toString ( ) ;
967
- let [ lhs , rhs ] = s . split ( "." ) ;
968
-
969
- if ( undefined === rhs ) {
970
- rhs = "" ;
971
+ if ( this . isZero ( ) ) {
972
+ return this . clone ( ) ;
971
973
}
972
974
973
- rhs = rhs + "0" . repeat ( numDecimalDigits ) ;
974
-
975
- let finalIntegerDigit = parseInt (
976
- numDecimalDigits > 0
977
- ? rhs . charAt ( numDecimalDigits - 1 )
978
- : lhs . charAt ( lhs . length - 1 )
979
- ) as Digit ;
980
- let firstDecimalDigit = parseInt ( rhs . charAt ( numDecimalDigits ) ) as Digit ;
981
-
982
- if ( Number . isNaN ( firstDecimalDigit ) ) {
983
- firstDecimalDigit = 0 ;
984
- }
975
+ let v = this . cohort as Rational ;
976
+ let q = this . quantum as number ;
985
977
986
- let roundedFinalDigit = roundIt (
987
- this . isNegative ( ) ,
988
- finalIntegerDigit ,
989
- firstDecimalDigit ,
990
- mode
991
- ) ;
992
- return new Decimal128 (
993
- numDecimalDigits > 0
994
- ? lhs +
995
- "." +
996
- rhs . substring ( 0 , numDecimalDigits - 1 ) +
997
- `${ roundedFinalDigit } `
998
- : lhs . substring ( 0 , lhs . length - 1 ) + `${ roundedFinalDigit } `
999
- ) ;
978
+ let roundedV = v . round ( numDecimalDigits , mode ) ;
979
+ return Decimal128 . fromCohortAndQuantum ( roundedV , q ) ;
1000
980
}
1001
981
1002
982
neg ( ) : Decimal128 {
0 commit comments