@@ -366,7 +366,7 @@ void BIGNUM::SetFromRgchExp(const EncodedChar *prgch, int32 cch, int32 lwExp)
366
366
367
367
while (++prgch < pchLim)
368
368
{
369
- if (*prgch == ' .' )
369
+ if (*prgch == ' .' || *prgch == ' _ ' )
370
370
continue ;
371
371
Assert (Js::NumberUtilities::IsDigit (*prgch));
372
372
MulTenAdd ((byte) (*prgch - ' 0' ), &luExtra);
@@ -893,7 +893,7 @@ static double AdjustDbl(double dbl, const EncodedChar *prgch, int32 cch, int32 l
893
893
String to Double.
894
894
***************************************************************************/
895
895
template <typename EncodedChar>
896
- double Js::NumberUtilities::StrToDbl ( const EncodedChar *psz, const EncodedChar **ppchLim, LikelyNumberType& likelyNumberType, bool isBigIntEnabled)
896
+ double Js::NumberUtilities::StrToDbl (const EncodedChar *psz, const EncodedChar **ppchLim, LikelyNumberType& likelyNumberType, bool isBigIntEnabled, bool isNumericSeparatorEnabled )
897
897
{
898
898
uint32 lu;
899
899
BIGNUM num;
@@ -909,6 +909,10 @@ double Js::NumberUtilities::StrToDbl( const EncodedChar *psz, const EncodedChar
909
909
Assert (Js::NumberUtilities::IsNan (dblLowPrec));
910
910
#endif // DBG
911
911
912
+ // Numeric separator characters exist in the numeric constant and should
913
+ // be ignored.
914
+ bool hasNumericSeparators = false ;
915
+
912
916
// For the mantissa digits. After leaving the state machine, pchMinDig
913
917
// points to the first digit and pchLimDig points just past the last
914
918
// digit. cchDig is the number of digits. pchLimDig - pchMinDig may be
@@ -979,8 +983,11 @@ double Js::NumberUtilities::StrToDbl( const EncodedChar *psz, const EncodedChar
979
983
if (Js::NumberUtilities::IsDigit (*pch))
980
984
{
981
985
LGetLeftDig:
982
- pchMinDig = pch;
983
- for (cchDig = 1 ; Js::NumberUtilities::IsDigit (*++pch); cchDig++)
986
+ if (pchMinDig == NULL )
987
+ {
988
+ pchMinDig = pch;
989
+ }
990
+ for (cchDig++; Js::NumberUtilities::IsDigit (*++pch); cchDig++)
984
991
;
985
992
}
986
993
switch (*pch)
@@ -995,6 +1002,26 @@ double Js::NumberUtilities::StrToDbl( const EncodedChar *psz, const EncodedChar
995
1002
{
996
1003
goto LBigInt;
997
1004
}
1005
+ goto LGetLeftDefault;
1006
+ case ' _' :
1007
+ if (isNumericSeparatorEnabled)
1008
+ {
1009
+ // A numeric separator is only valid if it appears between two
1010
+ // digits. If the preceeding or following character is not a digit,
1011
+ // we should just fallthrough and fail. Otherwise we would have to
1012
+ // handle cases like 1_.0 manually above.
1013
+ // cchDig holds the count of digits in the literal. If it's >0, we
1014
+ // can be sure the previous pch is valid.
1015
+ if (cchDig > 0 && Js::NumberUtilities::IsDigit (*(pch - 1 ))
1016
+ && Js::NumberUtilities::IsDigit (*(pch + 1 )))
1017
+ {
1018
+ hasNumericSeparators = true ;
1019
+ pch++;
1020
+ goto LGetLeftDig;
1021
+ }
1022
+ }
1023
+ // Fallthrough
1024
+ LGetLeftDefault:
998
1025
default :
999
1026
likelyNumberType = LikelyNumberType::Int;
1000
1027
}
@@ -1010,6 +1037,7 @@ double Js::NumberUtilities::StrToDbl( const EncodedChar *psz, const EncodedChar
1010
1037
lwAdj--;
1011
1038
pchMinDig = pch;
1012
1039
}
1040
+ LGetRightDigit:
1013
1041
for ( ; Js::NumberUtilities::IsDigit (*pch); pch++)
1014
1042
{
1015
1043
cchDig++;
@@ -1020,6 +1048,17 @@ double Js::NumberUtilities::StrToDbl( const EncodedChar *psz, const EncodedChar
1020
1048
case ' E' :
1021
1049
case ' e' :
1022
1050
goto LGetExp;
1051
+ case ' _' :
1052
+ if (isNumericSeparatorEnabled)
1053
+ {
1054
+ if (cchDig > 0 && Js::NumberUtilities::IsDigit (*(pch - 1 )) &&
1055
+ Js::NumberUtilities::IsDigit (*(pch + 1 )))
1056
+ {
1057
+ hasNumericSeparators = true ;
1058
+ pch++;
1059
+ goto LGetRightDigit;
1060
+ }
1061
+ }
1023
1062
}
1024
1063
goto LEnd;
1025
1064
@@ -1050,6 +1089,19 @@ double Js::NumberUtilities::StrToDbl( const EncodedChar *psz, const EncodedChar
1050
1089
if (lwExp > 100000000 )
1051
1090
lwExp = 100000000 ;
1052
1091
}
1092
+ switch (*pch)
1093
+ {
1094
+ case ' _' :
1095
+ if (isNumericSeparatorEnabled)
1096
+ {
1097
+ if (Js::NumberUtilities::IsDigit (*(pch - 1 )) &&
1098
+ Js::NumberUtilities::IsDigit (*(pch + 1 )))
1099
+ {
1100
+ pch++;
1101
+ goto LGetExpDigits;
1102
+ }
1103
+ }
1104
+ }
1053
1105
goto LEnd;
1054
1106
1055
1107
LBigInt:
@@ -1070,7 +1122,8 @@ double Js::NumberUtilities::StrToDbl( const EncodedChar *psz, const EncodedChar
1070
1122
pchLimDig = pch;
1071
1123
Assert (pchMinDig != NULL );
1072
1124
Assert (pchLimDig - pchMinDig == cchDig ||
1073
- pchLimDig - pchMinDig == cchDig + 1 );
1125
+ pchLimDig - pchMinDig == cchDig + 1 ||
1126
+ (isNumericSeparatorEnabled && hasNumericSeparators));
1074
1127
1075
1128
// Limit to kcchMaxSig digits.
1076
1129
if (cchDig > kcchMaxSig)
@@ -1116,15 +1169,16 @@ double Js::NumberUtilities::StrToDbl( const EncodedChar *psz, const EncodedChar
1116
1169
cchDig--;
1117
1170
lwAdj++;
1118
1171
}
1119
- else if (*pchLimDig != ' .' )
1172
+ else if (*pchLimDig != ' .' && *pchLimDig != ' _ ' )
1120
1173
{
1121
1174
Assert (FNzDigit (*pchLimDig));
1122
1175
pchLimDig++;
1123
1176
break ;
1124
1177
}
1125
1178
}
1126
1179
Assert (pchLimDig - pchMinDig == cchDig ||
1127
- pchLimDig - pchMinDig == cchDig + 1 );
1180
+ pchLimDig - pchMinDig == cchDig + 1 ||
1181
+ (isNumericSeparatorEnabled && hasNumericSeparators));
1128
1182
1129
1183
if (signExp < 0 )
1130
1184
lwExp = -lwExp;
@@ -1139,8 +1193,14 @@ double Js::NumberUtilities::StrToDbl( const EncodedChar *psz, const EncodedChar
1139
1193
// Can use the ALU.
1140
1194
for (lu = 0 , pch = pchMinDig; pch < pchLimDig; pch++)
1141
1195
{
1142
- if (*pch != ' . ' )
1196
+ switch (*pch)
1143
1197
{
1198
+ case ' .' :
1199
+ break ;
1200
+ case ' _' :
1201
+ Assert (isNumericSeparatorEnabled && hasNumericSeparators);
1202
+ break ;
1203
+ default :
1144
1204
Assert (Js::NumberUtilities::IsDigit (*pch));
1145
1205
lu = lu * 10 + (*pch - ' 0' );
1146
1206
}
@@ -1151,8 +1211,14 @@ double Js::NumberUtilities::StrToDbl( const EncodedChar *psz, const EncodedChar
1151
1211
{
1152
1212
for (dbl = 0 , pch = pchMinDig; pch < pchLimDig; pch++)
1153
1213
{
1154
- if (*pch != ' . ' )
1214
+ switch (*pch)
1155
1215
{
1216
+ case ' .' :
1217
+ break ;
1218
+ case ' _' :
1219
+ Assert (isNumericSeparatorEnabled && hasNumericSeparators);
1220
+ break ;
1221
+ default :
1156
1222
Assert (Js::NumberUtilities::IsDigit (*pch));
1157
1223
dbl = dbl * 10 + (*pch - ' 0' );
1158
1224
}
@@ -1270,8 +1336,8 @@ double Js::NumberUtilities::StrToDbl( const EncodedChar *psz, const EncodedChar
1270
1336
return dbl;
1271
1337
}
1272
1338
1273
- template double Js::NumberUtilities::StrToDbl<char16>( const char16 * psz, const char16 **ppchLim, LikelyNumberType& likelyInt, bool isBigIntEnabled );
1274
- template double Js::NumberUtilities::StrToDbl<utf8char_t >(const utf8char_t * psz, const utf8char_t **ppchLim, LikelyNumberType& likelyInt, bool isBigIntEnabled );
1339
+ template double Js::NumberUtilities::StrToDbl<char16>( const char16 * psz, const char16 **ppchLim, LikelyNumberType& likelyInt, bool isBigIntEnabled, bool isNumericSeparatorEnabled );
1340
+ template double Js::NumberUtilities::StrToDbl<utf8char_t >(const utf8char_t * psz, const utf8char_t **ppchLim, LikelyNumberType& likelyInt, bool isBigIntEnabled, bool isNumericSeparatorEnabled );
1275
1341
1276
1342
/* **************************************************************************
1277
1343
Uses big integer arithmetic to get the sequence of digits.
0 commit comments