@@ -23,45 +23,6 @@ const MAX_SIGNIFICANT_DIGITS = 34;
23
23
const bigTen = BigInt ( 10 ) ;
24
24
const bigOne = BigInt ( 1 ) ;
25
25
26
- /**
27
- * Normalize a digit string. This means:
28
- *
29
- * + removing any initial zeros
30
- * + removing any trailing zeros
31
- * + rewriting 0.0 to 0
32
- *
33
- * Sign is preserved. Thus, -0.0 (e.g.) gets normalized to -0.
34
- *
35
- * @param s A digit string
36
- *
37
- * @example normalize("000123.456000") // => "123.456"
38
- * @example normalize("000000.000000") // => "0"
39
- * @example normalize("000000.000001") // => "0.000001"
40
- * @example normalize("000000.100000") // => "0.1"
41
- */
42
- function normalize ( s : string ) : string {
43
- if ( s . match ( / ^ - / ) ) {
44
- return "-" + normalize ( s . substring ( 1 ) ) ;
45
- }
46
-
47
- let a = s . replace ( / ^ 0 + / , "" ) ;
48
- let b = a . match ( / [ . ] / ) ? a . replace ( / 0 + $ / , "" ) : a ;
49
-
50
- if ( b . match ( / ^ [ . ] / ) ) {
51
- b = "0" + b ;
52
- }
53
-
54
- if ( b . match ( / [ . ] $ / ) ) {
55
- b = b . substring ( 0 , b . length - 1 ) ;
56
- }
57
-
58
- if ( "" === b ) {
59
- b = "0" ;
60
- }
61
-
62
- return b ;
63
- }
64
-
65
26
/**
66
27
* Return the significand of a digit string, assumed to be normalized.
67
28
* The returned value is a digit string that has no decimal point, even if the original
@@ -123,6 +84,10 @@ function cutoffAfterSignificantDigits(s: string, n: number): string {
123
84
}
124
85
125
86
function ensureDecimalDigits ( s : string , n ?: number ) : string {
87
+ if ( s . match ( / ^ - / ) ) {
88
+ return "-" + ensureDecimalDigits ( s . substring ( 1 ) , n ) ;
89
+ }
90
+
126
91
if ( undefined === n ) {
127
92
return s ;
128
93
}
@@ -690,11 +655,6 @@ function handleDecimalNotation(
690
655
options : FullySpecifiedConstructorOptions
691
656
) : Decimal128Constructor {
692
657
let withoutUnderscores = s . replace ( / _ / g, "" ) ;
693
-
694
- if ( options . normalize ) {
695
- withoutUnderscores = normalize ( withoutUnderscores ) ;
696
- }
697
-
698
658
let isNegative = ! ! withoutUnderscores . match ( / ^ - / ) ;
699
659
let sg = significand ( withoutUnderscores ) ;
700
660
let exp = exponent ( withoutUnderscores ) ;
@@ -737,7 +697,7 @@ export const ROUNDING_MODE_HALF_FLOOR: RoundingMode = "halfFloor";
737
697
export const ROUNDING_MODE_HALF_TRUNCATE : RoundingMode = "halfTrunc" ;
738
698
739
699
const ROUNDING_MODE_DEFAULT = ROUNDING_MODE_HALF_EVEN ;
740
- const CONSTRUCTOR_SHOULD_NORMALIZE = true ;
700
+ const CONSTRUCTOR_SHOULD_NORMALIZE = false ;
741
701
742
702
function roundIt (
743
703
isNegative : boolean ,
@@ -890,16 +850,31 @@ const TOSTRING_FORMATS: string[] = ["decimal", "exponential"];
890
850
interface ToStringOptions {
891
851
format ?: ToStringFormat ;
892
852
numDecimalDigits ?: number ;
853
+ normalize ?: boolean ;
893
854
}
894
855
895
856
interface FullySpecifiedToStringOptions {
896
857
format : string ;
897
858
numDecimalDigits : number | undefined ;
859
+ normalize : boolean ;
898
860
}
899
861
900
862
const DEFAULT_TOSTRING_OPTIONS : FullySpecifiedToStringOptions = Object . freeze ( {
901
863
format : "decimal" ,
902
864
numDecimalDigits : undefined ,
865
+ normalize : true ,
866
+ } ) ;
867
+
868
+ interface CmpOptions {
869
+ normalize ?: boolean ;
870
+ }
871
+
872
+ interface FullySpecifiedCmpOptions {
873
+ normalize : boolean ;
874
+ }
875
+
876
+ const DEFAULT_CMP_OPTIONS : FullySpecifiedCmpOptions = Object . freeze ( {
877
+ normalize : true ,
903
878
} ) ;
904
879
905
880
function ensureFullySpecifiedConstructorOptions (
@@ -967,6 +942,26 @@ function ensureFullySpecifiedToStringOptions(
967
942
opts . numDecimalDigits = options . numDecimalDigits ;
968
943
}
969
944
945
+ if ( "boolean" === typeof options . normalize ) {
946
+ opts . normalize = options . normalize ;
947
+ }
948
+
949
+ return opts ;
950
+ }
951
+
952
+ function ensureFullySpecifiedCmpOptions (
953
+ options ?: CmpOptions
954
+ ) : FullySpecifiedCmpOptions {
955
+ let opts = { ...DEFAULT_CMP_OPTIONS } ;
956
+
957
+ if ( undefined === options ) {
958
+ return opts ;
959
+ }
960
+
961
+ if ( "boolean" === typeof options . normalize ) {
962
+ opts . normalize = options . normalize ;
963
+ }
964
+
970
965
return opts ;
971
966
}
972
967
@@ -1076,11 +1071,39 @@ export class Decimal128 {
1076
1071
return ( this . isNegative ? "-" : "" ) + POSITIVE_INFINITY ;
1077
1072
}
1078
1073
1079
- let prefix = this . isNegative ? "-" : "" ;
1074
+ let neg = this . isNegative ;
1075
+ let prefix = neg ? "-" : "" ;
1080
1076
let sg = this . significand ;
1081
1077
let exp = this . exponent ;
1078
+ let isZ = this . isZero ( ) ;
1079
+ let numFractionalDigits = options . numDecimalDigits ;
1080
+
1081
+ if (
1082
+ "number" === typeof numFractionalDigits &&
1083
+ numFractionalDigits < 0
1084
+ ) {
1085
+ numFractionalDigits = undefined ;
1086
+ }
1087
+
1088
+ let renderedRat = this . rat . toDecimalPlaces (
1089
+ numFractionalDigits ?? MAX_SIGNIFICANT_DIGITS
1090
+ ) ;
1082
1091
1083
1092
function emitDecimal ( ) : string {
1093
+ if ( options . normalize && options . numDecimalDigits === undefined ) {
1094
+ if ( isZ ) {
1095
+ if ( neg ) {
1096
+ return "-0" ;
1097
+ }
1098
+
1099
+ return "0" ;
1100
+ }
1101
+ return ensureDecimalDigits (
1102
+ renderedRat ,
1103
+ options . numDecimalDigits
1104
+ ) ;
1105
+ }
1106
+
1084
1107
if ( exp >= 0 ) {
1085
1108
return ensureDecimalDigits (
1086
1109
prefix + sg + "0" . repeat ( exp ) ,
@@ -1144,8 +1167,9 @@ export class Decimal128 {
1144
1167
* + 1 otherwise.
1145
1168
*
1146
1169
* @param x
1170
+ * @param opts
1147
1171
*/
1148
- cmp ( x : Decimal128 ) : - 1 | 0 | 1 | undefined {
1172
+ cmp ( x : Decimal128 , opts ?: CmpOptions ) : - 1 | 0 | 1 | undefined {
1149
1173
if ( this . isNaN ( ) || x . isNaN ( ) ) {
1150
1174
return undefined ;
1151
1175
}
@@ -1170,7 +1194,25 @@ export class Decimal128 {
1170
1194
return x . isNegative ? 1 : - 1 ;
1171
1195
}
1172
1196
1173
- return this . rat . cmp ( x . rat ) ;
1197
+ let options = ensureFullySpecifiedCmpOptions ( opts ) ;
1198
+
1199
+ let ratCmp = this . rat . cmp ( x . rat ) ;
1200
+
1201
+ if ( ratCmp !== 0 ) {
1202
+ return ratCmp ;
1203
+ }
1204
+
1205
+ if ( this . isZero ( ) || options . normalize ) {
1206
+ return 0 ;
1207
+ }
1208
+
1209
+ let renderedThis = this . toString ( {
1210
+ format : "decimal" ,
1211
+ normalize : false ,
1212
+ } ) ;
1213
+ let renderedThat = x . toString ( { format : "decimal" , normalize : false } ) ;
1214
+
1215
+ return renderedThis > renderedThat ? - 1 : 1 ;
1174
1216
}
1175
1217
1176
1218
/**
@@ -1214,7 +1256,7 @@ export class Decimal128 {
1214
1256
Math . min ( this . exponent , x . exponent )
1215
1257
) ;
1216
1258
1217
- return new Decimal128 ( adjusted . toString ( ) , { normalize : false } ) ;
1259
+ return new Decimal128 ( adjusted . toString ( { normalize : false } ) ) ;
1218
1260
}
1219
1261
1220
1262
/**
@@ -1258,7 +1300,7 @@ export class Decimal128 {
1258
1300
let adjusted = initialResult . setExponent (
1259
1301
Math . min ( this . exponent , x . exponent )
1260
1302
) ;
1261
- return new Decimal128 ( adjusted . toString ( ) , { normalize : false } ) ;
1303
+ return new Decimal128 ( adjusted . toString ( { normalize : false } ) ) ;
1262
1304
}
1263
1305
1264
1306
/**
@@ -1313,11 +1355,11 @@ export class Decimal128 {
1313
1355
) ;
1314
1356
let adjusted = initialResult . setExponent ( this . exponent + x . exponent ) ;
1315
1357
1316
- return new Decimal128 ( adjusted . toString ( ) , { normalize : false } ) ;
1358
+ return new Decimal128 ( adjusted . toString ( { normalize : false } ) ) ;
1317
1359
}
1318
1360
1319
1361
private isZero ( ) : boolean {
1320
- return this . isFinite ( ) && this . significand === "0" ;
1362
+ return this . isFinite ( ) && ! ! this . significand . match ( / ^ 0 + $ / ) ;
1321
1363
}
1322
1364
1323
1365
private clone ( ) : Decimal128 {
@@ -1432,23 +1474,15 @@ export class Decimal128 {
1432
1474
numDecimalDigits : number = 0 ,
1433
1475
mode : RoundingMode = ROUNDING_MODE_DEFAULT
1434
1476
) : Decimal128 {
1435
- if ( ! Number . isSafeInteger ( numDecimalDigits ) ) {
1436
- throw new RangeError (
1437
- "Argument for number of decimal digits must be a safe integer"
1438
- ) ;
1477
+ if ( this . isNaN ( ) || ! this . isFinite ( ) ) {
1478
+ return this . clone ( ) ;
1439
1479
}
1440
1480
1441
1481
if ( numDecimalDigits < 0 ) {
1442
- throw new RangeError (
1443
- "Argument for number of decimal digits must be non-negative"
1444
- ) ;
1445
- }
1446
-
1447
- if ( this . isNaN ( ) || ! this . isFinite ( ) ) {
1448
- return this ;
1482
+ numDecimalDigits = 0 ;
1449
1483
}
1450
1484
1451
- let s = this . toString ( ) ;
1485
+ let s = this . toString ( { normalize : false } ) ;
1452
1486
let [ lhs , rhs ] = s . split ( "." ) ;
1453
1487
1454
1488
if ( undefined === rhs ) {
@@ -1485,13 +1519,13 @@ export class Decimal128 {
1485
1519
}
1486
1520
1487
1521
private negate ( ) : Decimal128 {
1488
- let s = this . toString ( ) ;
1522
+ let s = this . toString ( { normalize : false } ) ;
1489
1523
1490
1524
if ( s . match ( / ^ - / ) ) {
1491
- return new Decimal128 ( s . substring ( 1 ) , { normalize : false } ) ;
1525
+ return new Decimal128 ( s . substring ( 1 ) ) ;
1492
1526
}
1493
1527
1494
- return new Decimal128 ( "-" + s , { normalize : false } ) ;
1528
+ return new Decimal128 ( "-" + s ) ;
1495
1529
}
1496
1530
1497
1531
/**
@@ -1530,10 +1564,6 @@ export class Decimal128 {
1530
1564
return this . subtract ( d . multiply ( q ) , opts ) ;
1531
1565
}
1532
1566
1533
- normalize ( ) : Decimal128 {
1534
- return new Decimal128 ( normalize ( this . toString ( ) ) ) ;
1535
- }
1536
-
1537
1567
private decrementExponent ( ) : Decimal128 {
1538
1568
let exp = this . exponent ;
1539
1569
let sig = this . significand ;
@@ -1542,9 +1572,7 @@ export class Decimal128 {
1542
1572
let newExp = exp - 1 ;
1543
1573
let newSig = sig + "0" ;
1544
1574
1545
- return new Decimal128 ( `${ prefix } ${ newSig } E${ newExp } ` , {
1546
- normalize : false ,
1547
- } ) ;
1575
+ return new Decimal128 ( `${ prefix } ${ newSig } E${ newExp } ` ) ;
1548
1576
}
1549
1577
1550
1578
private setExponent ( newExp : number ) : Decimal128 {
0 commit comments