Number.Parsing.cs 74 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Diagnostics;
  5. using System.Globalization;
  6. using System.Runtime.CompilerServices;
  7. using System.Runtime.InteropServices;
  8. using Internal.Runtime.CompilerServices;
  9. namespace System
  10. {
  11. // The Parse methods provided by the numeric classes convert a
  12. // string to a numeric value. The optional style parameter specifies the
  13. // permitted style of the numeric string. It must be a combination of bit flags
  14. // from the NumberStyles enumeration. The optional info parameter
  15. // specifies the NumberFormatInfo instance to use when parsing the
  16. // string. If the info parameter is null or omitted, the numeric
  17. // formatting information is obtained from the current culture.
  18. //
  19. // Numeric strings produced by the Format methods using the Currency,
  20. // Decimal, Engineering, Fixed point, General, or Number standard formats
  21. // (the C, D, E, F, G, and N format specifiers) are guaranteed to be parseable
  22. // by the Parse methods if the NumberStyles.Any style is
  23. // specified. Note, however, that the Parse methods do not accept
  24. // NaNs or Infinities.
  25. internal partial class Number
  26. {
  27. private const int Int32Precision = 10;
  28. private const int UInt32Precision = Int32Precision;
  29. private const int Int64Precision = 19;
  30. private const int UInt64Precision = 20;
  31. /// <summary>Map from an ASCII char to its hex value, e.g. arr['b'] == 11. 0xFF means it's not a hex digit.</summary>
  32. internal static ReadOnlySpan<byte> CharToHexLookup => new byte[]
  33. {
  34. 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 15
  35. 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 31
  36. 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 47
  37. 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 63
  38. 0xFF, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 79
  39. 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 95
  40. 0xFF, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf // 102
  41. };
  42. private static unsafe bool TryNumberToInt32(ref NumberBuffer number, ref int value)
  43. {
  44. number.CheckConsistency();
  45. int i = number.Scale;
  46. if (i > Int32Precision || i < number.DigitsCount)
  47. {
  48. return false;
  49. }
  50. byte* p = number.GetDigitsPointer();
  51. Debug.Assert(p != null);
  52. int n = 0;
  53. while (--i >= 0)
  54. {
  55. if ((uint)n > (0x7FFFFFFF / 10))
  56. {
  57. return false;
  58. }
  59. n *= 10;
  60. if (*p != '\0')
  61. {
  62. n += (*p++ - '0');
  63. }
  64. }
  65. if (number.IsNegative)
  66. {
  67. n = -n;
  68. if (n > 0)
  69. {
  70. return false;
  71. }
  72. }
  73. else
  74. {
  75. if (n < 0)
  76. {
  77. return false;
  78. }
  79. }
  80. value = n;
  81. return true;
  82. }
  83. private static unsafe bool TryNumberToInt64(ref NumberBuffer number, ref long value)
  84. {
  85. number.CheckConsistency();
  86. int i = number.Scale;
  87. if (i > Int64Precision || i < number.DigitsCount)
  88. {
  89. return false;
  90. }
  91. byte* p = number.GetDigitsPointer();
  92. Debug.Assert(p != null);
  93. long n = 0;
  94. while (--i >= 0)
  95. {
  96. if ((ulong)n > (0x7FFFFFFFFFFFFFFF / 10))
  97. {
  98. return false;
  99. }
  100. n *= 10;
  101. if (*p != '\0')
  102. {
  103. n += (*p++ - '0');
  104. }
  105. }
  106. if (number.IsNegative)
  107. {
  108. n = -n;
  109. if (n > 0)
  110. {
  111. return false;
  112. }
  113. }
  114. else
  115. {
  116. if (n < 0)
  117. {
  118. return false;
  119. }
  120. }
  121. value = n;
  122. return true;
  123. }
  124. private static unsafe bool TryNumberToUInt32(ref NumberBuffer number, ref uint value)
  125. {
  126. number.CheckConsistency();
  127. int i = number.Scale;
  128. if (i > UInt32Precision || i < number.DigitsCount || number.IsNegative)
  129. {
  130. return false;
  131. }
  132. byte* p = number.GetDigitsPointer();
  133. Debug.Assert(p != null);
  134. uint n = 0;
  135. while (--i >= 0)
  136. {
  137. if (n > (0xFFFFFFFF / 10))
  138. {
  139. return false;
  140. }
  141. n *= 10;
  142. if (*p != '\0')
  143. {
  144. uint newN = n + (uint)(*p++ - '0');
  145. // Detect an overflow here...
  146. if (newN < n)
  147. {
  148. return false;
  149. }
  150. n = newN;
  151. }
  152. }
  153. value = n;
  154. return true;
  155. }
  156. private static unsafe bool TryNumberToUInt64(ref NumberBuffer number, ref ulong value)
  157. {
  158. number.CheckConsistency();
  159. int i = number.Scale;
  160. if (i > UInt64Precision || i < number.DigitsCount || number.IsNegative)
  161. {
  162. return false;
  163. }
  164. byte* p = number.GetDigitsPointer();
  165. Debug.Assert(p != null);
  166. ulong n = 0;
  167. while (--i >= 0)
  168. {
  169. if (n > (0xFFFFFFFFFFFFFFFF / 10))
  170. {
  171. return false;
  172. }
  173. n *= 10;
  174. if (*p != '\0')
  175. {
  176. ulong newN = n + (ulong)(*p++ - '0');
  177. // Detect an overflow here...
  178. if (newN < n)
  179. {
  180. return false;
  181. }
  182. n = newN;
  183. }
  184. }
  185. value = n;
  186. return true;
  187. }
  188. internal static int ParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
  189. {
  190. ParsingStatus status = TryParseInt32(value, styles, info, out int result);
  191. if (status != ParsingStatus.OK)
  192. {
  193. ThrowOverflowOrFormatException(status, TypeCode.Int32);
  194. }
  195. return result;
  196. }
  197. internal static long ParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
  198. {
  199. ParsingStatus status = TryParseInt64(value, styles, info, out long result);
  200. if (status != ParsingStatus.OK)
  201. {
  202. ThrowOverflowOrFormatException(status, TypeCode.Int64);
  203. }
  204. return result;
  205. }
  206. internal static uint ParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
  207. {
  208. ParsingStatus status = TryParseUInt32(value, styles, info, out uint result);
  209. if (status != ParsingStatus.OK)
  210. {
  211. ThrowOverflowOrFormatException(status, TypeCode.UInt32);
  212. }
  213. return result;
  214. }
  215. internal static ulong ParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
  216. {
  217. ParsingStatus status = TryParseUInt64(value, styles, info, out ulong result);
  218. if (status != ParsingStatus.OK)
  219. {
  220. ThrowOverflowOrFormatException(status, TypeCode.UInt64);
  221. }
  222. return result;
  223. }
  224. private static unsafe bool TryParseNumber(ref char* str, char* strEnd, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
  225. {
  226. Debug.Assert(str != null);
  227. Debug.Assert(strEnd != null);
  228. Debug.Assert(str <= strEnd);
  229. Debug.Assert((styles & NumberStyles.AllowHexSpecifier) == 0);
  230. const int StateSign = 0x0001;
  231. const int StateParens = 0x0002;
  232. const int StateDigits = 0x0004;
  233. const int StateNonZero = 0x0008;
  234. const int StateDecimal = 0x0010;
  235. const int StateCurrency = 0x0020;
  236. Debug.Assert(number.DigitsCount == 0);
  237. Debug.Assert(number.Scale == 0);
  238. Debug.Assert(number.IsNegative == false);
  239. Debug.Assert(number.HasNonZeroTail == false);
  240. number.CheckConsistency();
  241. string decSep; // decimal separator from NumberFormatInfo.
  242. string groupSep; // group separator from NumberFormatInfo.
  243. string currSymbol = null; // currency symbol from NumberFormatInfo.
  244. bool parsingCurrency = false;
  245. if ((styles & NumberStyles.AllowCurrencySymbol) != 0)
  246. {
  247. currSymbol = info.CurrencySymbol;
  248. // The idea here is to match the currency separators and on failure match the number separators to keep the perf of VB's IsNumeric fast.
  249. // The values of decSep are setup to use the correct relevant separator (currency in the if part and decimal in the else part).
  250. decSep = info.CurrencyDecimalSeparator;
  251. groupSep = info.CurrencyGroupSeparator;
  252. parsingCurrency = true;
  253. }
  254. else
  255. {
  256. decSep = info.NumberDecimalSeparator;
  257. groupSep = info.NumberGroupSeparator;
  258. }
  259. int state = 0;
  260. char* p = str;
  261. char ch = p < strEnd ? *p : '\0';
  262. char* next;
  263. while (true)
  264. {
  265. // Eat whitespace unless we've found a sign which isn't followed by a currency symbol.
  266. // "-Kr 1231.47" is legal but "- 1231.47" is not.
  267. if (!IsWhite(ch) || (styles & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && info.NumberNegativePattern != 2)))
  268. {
  269. if ((((styles & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || ((next = MatchChars(p, strEnd, info.NegativeSign)) != null && (number.IsNegative = true))))
  270. {
  271. state |= StateSign;
  272. p = next - 1;
  273. }
  274. else if (ch == '(' && ((styles & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0))
  275. {
  276. state |= StateSign | StateParens;
  277. number.IsNegative = true;
  278. }
  279. else if (currSymbol != null && (next = MatchChars(p, strEnd, currSymbol)) != null)
  280. {
  281. state |= StateCurrency;
  282. currSymbol = null;
  283. // We already found the currency symbol. There should not be more currency symbols. Set
  284. // currSymbol to NULL so that we won't search it again in the later code path.
  285. p = next - 1;
  286. }
  287. else
  288. {
  289. break;
  290. }
  291. }
  292. ch = ++p < strEnd ? *p : '\0';
  293. }
  294. int digCount = 0;
  295. int digEnd = 0;
  296. int maxDigCount = number.Digits.Length - 1;
  297. while (true)
  298. {
  299. if (IsDigit(ch))
  300. {
  301. state |= StateDigits;
  302. if (ch != '0' || (state & StateNonZero) != 0)
  303. {
  304. if (digCount < maxDigCount)
  305. {
  306. number.Digits[digCount++] = (byte)(ch);
  307. if ((ch != '0') || (number.Kind != NumberBufferKind.Integer))
  308. {
  309. digEnd = digCount;
  310. }
  311. }
  312. else if (ch != '0')
  313. {
  314. // For decimal and binary floating-point numbers, we only
  315. // need to store digits up to maxDigCount. However, we still
  316. // need to keep track of whether any additional digits past
  317. // maxDigCount were non-zero, as that can impact rounding
  318. // for an input that falls evenly between two representable
  319. // results.
  320. number.HasNonZeroTail = true;
  321. }
  322. if ((state & StateDecimal) == 0)
  323. {
  324. number.Scale++;
  325. }
  326. state |= StateNonZero;
  327. }
  328. else if ((state & StateDecimal) != 0)
  329. {
  330. number.Scale--;
  331. }
  332. }
  333. else if (((styles & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, decSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, info.NumberDecimalSeparator)) != null))
  334. {
  335. state |= StateDecimal;
  336. p = next - 1;
  337. }
  338. else if (((styles & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, groupSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, info.NumberGroupSeparator)) != null))
  339. {
  340. p = next - 1;
  341. }
  342. else
  343. {
  344. break;
  345. }
  346. ch = ++p < strEnd ? *p : '\0';
  347. }
  348. bool negExp = false;
  349. number.DigitsCount = digEnd;
  350. number.Digits[digEnd] = (byte)('\0');
  351. if ((state & StateDigits) != 0)
  352. {
  353. if ((ch == 'E' || ch == 'e') && ((styles & NumberStyles.AllowExponent) != 0))
  354. {
  355. char* temp = p;
  356. ch = ++p < strEnd ? *p : '\0';
  357. if ((next = MatchChars(p, strEnd, info.positiveSign)) != null)
  358. {
  359. ch = (p = next) < strEnd ? *p : '\0';
  360. }
  361. else if ((next = MatchChars(p, strEnd, info.negativeSign)) != null)
  362. {
  363. ch = (p = next) < strEnd ? *p : '\0';
  364. negExp = true;
  365. }
  366. if (IsDigit(ch))
  367. {
  368. int exp = 0;
  369. do
  370. {
  371. exp = exp * 10 + (ch - '0');
  372. ch = ++p < strEnd ? *p : '\0';
  373. if (exp > 1000)
  374. {
  375. exp = 9999;
  376. while (IsDigit(ch))
  377. {
  378. ch = ++p < strEnd ? *p : '\0';
  379. }
  380. }
  381. } while (IsDigit(ch));
  382. if (negExp)
  383. {
  384. exp = -exp;
  385. }
  386. number.Scale += exp;
  387. }
  388. else
  389. {
  390. p = temp;
  391. ch = p < strEnd ? *p : '\0';
  392. }
  393. }
  394. while (true)
  395. {
  396. if (!IsWhite(ch) || (styles & NumberStyles.AllowTrailingWhite) == 0)
  397. {
  398. if (((styles & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0)) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || (((next = MatchChars(p, strEnd, info.NegativeSign)) != null) && (number.IsNegative = true))))
  399. {
  400. state |= StateSign;
  401. p = next - 1;
  402. }
  403. else if (ch == ')' && ((state & StateParens) != 0))
  404. {
  405. state &= ~StateParens;
  406. }
  407. else if (currSymbol != null && (next = MatchChars(p, strEnd, currSymbol)) != null)
  408. {
  409. currSymbol = null;
  410. p = next - 1;
  411. }
  412. else
  413. {
  414. break;
  415. }
  416. }
  417. ch = ++p < strEnd ? *p : '\0';
  418. }
  419. if ((state & StateParens) == 0)
  420. {
  421. if ((state & StateNonZero) == 0)
  422. {
  423. if (number.Kind != NumberBufferKind.Decimal)
  424. {
  425. number.Scale = 0;
  426. }
  427. if ((number.Kind == NumberBufferKind.Integer) && (state & StateDecimal) == 0)
  428. {
  429. number.IsNegative = false;
  430. }
  431. }
  432. str = p;
  433. return true;
  434. }
  435. }
  436. str = p;
  437. return false;
  438. }
  439. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  440. internal static ParsingStatus TryParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
  441. {
  442. if ((styles & ~NumberStyles.Integer) == 0)
  443. {
  444. // Optimized path for the common case of anything that's allowed for integer style.
  445. return TryParseInt32IntegerStyle(value, styles, info, out result);
  446. }
  447. if ((styles & NumberStyles.AllowHexSpecifier) != 0)
  448. {
  449. result = 0;
  450. return TryParseUInt32HexNumberStyle(value, styles, out Unsafe.As<int, uint>(ref result));
  451. }
  452. return TryParseInt32Number(value, styles, info, out result);
  453. }
  454. private static unsafe ParsingStatus TryParseInt32Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
  455. {
  456. result = 0;
  457. byte* pDigits = stackalloc byte[Int32NumberBufferLength];
  458. NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
  459. if (!TryStringToNumber(value, styles, ref number, info))
  460. {
  461. return ParsingStatus.Failed;
  462. }
  463. if (!TryNumberToInt32(ref number, ref result))
  464. {
  465. return ParsingStatus.Overflow;
  466. }
  467. return ParsingStatus.OK;
  468. }
  469. /// <summary>Parses int limited to styles that make up NumberStyles.Integer.</summary>
  470. internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
  471. {
  472. Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
  473. if (value.IsEmpty)
  474. goto FalseExit;
  475. int index = 0;
  476. int num = value[0];
  477. // Skip past any whitespace at the beginning.
  478. if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
  479. {
  480. do
  481. {
  482. index++;
  483. if ((uint)index >= (uint)value.Length)
  484. goto FalseExit;
  485. num = value[index];
  486. }
  487. while (IsWhite(num));
  488. }
  489. // Parse leading sign.
  490. int sign = 1;
  491. if ((styles & NumberStyles.AllowLeadingSign) != 0)
  492. {
  493. if (info.HasInvariantNumberSigns)
  494. {
  495. if (num == '-')
  496. {
  497. sign = -1;
  498. index++;
  499. if ((uint)index >= (uint)value.Length)
  500. goto FalseExit;
  501. num = value[index];
  502. }
  503. else if (num == '+')
  504. {
  505. index++;
  506. if ((uint)index >= (uint)value.Length)
  507. goto FalseExit;
  508. num = value[index];
  509. }
  510. }
  511. else
  512. {
  513. value = value.Slice(index);
  514. index = 0;
  515. string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
  516. if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
  517. {
  518. index += positiveSign.Length;
  519. if ((uint)index >= (uint)value.Length)
  520. goto FalseExit;
  521. num = value[index];
  522. }
  523. else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
  524. {
  525. sign = -1;
  526. index += negativeSign.Length;
  527. if ((uint)index >= (uint)value.Length)
  528. goto FalseExit;
  529. num = value[index];
  530. }
  531. }
  532. }
  533. bool overflow = false;
  534. int answer = 0;
  535. if (IsDigit(num))
  536. {
  537. // Skip past leading zeros.
  538. if (num == '0')
  539. {
  540. do
  541. {
  542. index++;
  543. if ((uint)index >= (uint)value.Length)
  544. goto DoneAtEnd;
  545. num = value[index];
  546. } while (num == '0');
  547. if (!IsDigit(num))
  548. goto HasTrailingChars;
  549. }
  550. // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
  551. answer = num - '0'; // first digit
  552. index++;
  553. for (int i = 0; i < 8; i++) // next 8 digits can't overflow
  554. {
  555. if ((uint)index >= (uint)value.Length)
  556. goto DoneAtEnd;
  557. num = value[index];
  558. if (!IsDigit(num))
  559. goto HasTrailingChars;
  560. index++;
  561. answer = 10 * answer + num - '0';
  562. }
  563. if ((uint)index >= (uint)value.Length)
  564. goto DoneAtEnd;
  565. num = value[index];
  566. if (!IsDigit(num))
  567. goto HasTrailingChars;
  568. index++;
  569. // Potential overflow now processing the 10th digit.
  570. overflow = answer > int.MaxValue / 10;
  571. answer = answer * 10 + num - '0';
  572. overflow |= (uint)answer > int.MaxValue + (((uint)sign) >> 31);
  573. if ((uint)index >= (uint)value.Length)
  574. goto DoneAtEndButPotentialOverflow;
  575. // At this point, we're either overflowing or hitting a formatting error.
  576. // Format errors take precedence for compatibility.
  577. num = value[index];
  578. while (IsDigit(num))
  579. {
  580. overflow = true;
  581. index++;
  582. if ((uint)index >= (uint)value.Length)
  583. goto OverflowExit;
  584. num = value[index];
  585. }
  586. goto HasTrailingChars;
  587. }
  588. goto FalseExit;
  589. DoneAtEndButPotentialOverflow:
  590. if (overflow)
  591. {
  592. goto OverflowExit;
  593. }
  594. DoneAtEnd:
  595. result = answer * sign;
  596. ParsingStatus status = ParsingStatus.OK;
  597. Exit:
  598. return status;
  599. FalseExit: // parsing failed
  600. result = 0;
  601. status = ParsingStatus.Failed;
  602. goto Exit;
  603. OverflowExit:
  604. result = 0;
  605. status = ParsingStatus.Overflow;
  606. goto Exit;
  607. HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
  608. // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
  609. if (IsWhite(num))
  610. {
  611. if ((styles & NumberStyles.AllowTrailingWhite) == 0)
  612. goto FalseExit;
  613. for (index++; index < value.Length; index++)
  614. {
  615. if (!IsWhite(value[index]))
  616. break;
  617. }
  618. if ((uint)index >= (uint)value.Length)
  619. goto DoneAtEndButPotentialOverflow;
  620. }
  621. if (!TrailingZeros(value, index))
  622. goto FalseExit;
  623. goto DoneAtEndButPotentialOverflow;
  624. }
  625. /// <summary>Parses long inputs limited to styles that make up NumberStyles.Integer.</summary>
  626. internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
  627. {
  628. Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
  629. if (value.IsEmpty)
  630. goto FalseExit;
  631. int index = 0;
  632. int num = value[0];
  633. // Skip past any whitespace at the beginning.
  634. if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
  635. {
  636. do
  637. {
  638. index++;
  639. if ((uint)index >= (uint)value.Length)
  640. goto FalseExit;
  641. num = value[index];
  642. }
  643. while (IsWhite(num));
  644. }
  645. // Parse leading sign.
  646. int sign = 1;
  647. if ((styles & NumberStyles.AllowLeadingSign) != 0)
  648. {
  649. if (info.HasInvariantNumberSigns)
  650. {
  651. if (num == '-')
  652. {
  653. sign = -1;
  654. index++;
  655. if ((uint)index >= (uint)value.Length)
  656. goto FalseExit;
  657. num = value[index];
  658. }
  659. else if (num == '+')
  660. {
  661. index++;
  662. if ((uint)index >= (uint)value.Length)
  663. goto FalseExit;
  664. num = value[index];
  665. }
  666. }
  667. else
  668. {
  669. value = value.Slice(index);
  670. index = 0;
  671. string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
  672. if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
  673. {
  674. index += positiveSign.Length;
  675. if ((uint)index >= (uint)value.Length)
  676. goto FalseExit;
  677. num = value[index];
  678. }
  679. else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
  680. {
  681. sign = -1;
  682. index += negativeSign.Length;
  683. if ((uint)index >= (uint)value.Length)
  684. goto FalseExit;
  685. num = value[index];
  686. }
  687. }
  688. }
  689. bool overflow = false;
  690. long answer = 0;
  691. if (IsDigit(num))
  692. {
  693. // Skip past leading zeros.
  694. if (num == '0')
  695. {
  696. do
  697. {
  698. index++;
  699. if ((uint)index >= (uint)value.Length)
  700. goto DoneAtEnd;
  701. num = value[index];
  702. } while (num == '0');
  703. if (!IsDigit(num))
  704. goto HasTrailingChars;
  705. }
  706. // Parse most digits, up to the potential for overflow, which can't happen until after 18 digits.
  707. answer = num - '0'; // first digit
  708. index++;
  709. for (int i = 0; i < 17; i++) // next 17 digits can't overflow
  710. {
  711. if ((uint)index >= (uint)value.Length)
  712. goto DoneAtEnd;
  713. num = value[index];
  714. if (!IsDigit(num))
  715. goto HasTrailingChars;
  716. index++;
  717. answer = 10 * answer + num - '0';
  718. }
  719. if ((uint)index >= (uint)value.Length)
  720. goto DoneAtEnd;
  721. num = value[index];
  722. if (!IsDigit(num))
  723. goto HasTrailingChars;
  724. index++;
  725. // Potential overflow now processing the 19th digit.
  726. overflow = answer > long.MaxValue / 10;
  727. answer = answer * 10 + num - '0';
  728. overflow |= (ulong)answer > (ulong)long.MaxValue + (((uint)sign) >> 31);
  729. if ((uint)index >= (uint)value.Length)
  730. goto DoneAtEndButPotentialOverflow;
  731. // At this point, we're either overflowing or hitting a formatting error.
  732. // Format errors take precedence for compatibility.
  733. num = value[index];
  734. while (IsDigit(num))
  735. {
  736. overflow = true;
  737. index++;
  738. if ((uint)index >= (uint)value.Length)
  739. goto OverflowExit;
  740. num = value[index];
  741. }
  742. goto HasTrailingChars;
  743. }
  744. goto FalseExit;
  745. DoneAtEndButPotentialOverflow:
  746. if (overflow)
  747. {
  748. goto OverflowExit;
  749. }
  750. DoneAtEnd:
  751. result = answer * sign;
  752. ParsingStatus status = ParsingStatus.OK;
  753. Exit:
  754. return status;
  755. FalseExit: // parsing failed
  756. result = 0;
  757. status = ParsingStatus.Failed;
  758. goto Exit;
  759. OverflowExit:
  760. result = 0;
  761. status = ParsingStatus.Overflow;
  762. goto Exit;
  763. HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
  764. // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
  765. if (IsWhite(num))
  766. {
  767. if ((styles & NumberStyles.AllowTrailingWhite) == 0)
  768. goto FalseExit;
  769. for (index++; index < value.Length; index++)
  770. {
  771. if (!IsWhite(value[index]))
  772. break;
  773. }
  774. if ((uint)index >= (uint)value.Length)
  775. goto DoneAtEndButPotentialOverflow;
  776. }
  777. if (!TrailingZeros(value, index))
  778. goto FalseExit;
  779. goto DoneAtEndButPotentialOverflow;
  780. }
  781. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  782. internal static ParsingStatus TryParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
  783. {
  784. if ((styles & ~NumberStyles.Integer) == 0)
  785. {
  786. // Optimized path for the common case of anything that's allowed for integer style.
  787. return TryParseInt64IntegerStyle(value, styles, info, out result);
  788. }
  789. if ((styles & NumberStyles.AllowHexSpecifier) != 0)
  790. {
  791. result = 0;
  792. return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As<long, ulong>(ref result));
  793. }
  794. return TryParseInt64Number(value, styles, info, out result);
  795. }
  796. private static unsafe ParsingStatus TryParseInt64Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
  797. {
  798. result = 0;
  799. byte* pDigits = stackalloc byte[Int64NumberBufferLength];
  800. NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
  801. if (!TryStringToNumber(value, styles, ref number, info))
  802. {
  803. return ParsingStatus.Failed;
  804. }
  805. if (!TryNumberToInt64(ref number, ref result))
  806. {
  807. return ParsingStatus.Overflow;
  808. }
  809. return ParsingStatus.OK;
  810. }
  811. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  812. internal static ParsingStatus TryParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
  813. {
  814. if ((styles & ~NumberStyles.Integer) == 0)
  815. {
  816. // Optimized path for the common case of anything that's allowed for integer style.
  817. return TryParseUInt32IntegerStyle(value, styles, info, out result);
  818. }
  819. if ((styles & NumberStyles.AllowHexSpecifier) != 0)
  820. {
  821. return TryParseUInt32HexNumberStyle(value, styles, out result);
  822. }
  823. return TryParseUInt32Number(value, styles, info, out result);
  824. }
  825. private static unsafe ParsingStatus TryParseUInt32Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
  826. {
  827. result = 0;
  828. byte* pDigits = stackalloc byte[UInt32NumberBufferLength];
  829. NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
  830. if (!TryStringToNumber(value, styles, ref number, info))
  831. {
  832. return ParsingStatus.Failed;
  833. }
  834. if (!TryNumberToUInt32(ref number, ref result))
  835. {
  836. return ParsingStatus.Overflow;
  837. }
  838. return ParsingStatus.OK;
  839. }
  840. /// <summary>Parses uint limited to styles that make up NumberStyles.Integer.</summary>
  841. internal static ParsingStatus TryParseUInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
  842. {
  843. Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
  844. if (value.IsEmpty)
  845. goto FalseExit;
  846. int index = 0;
  847. int num = value[0];
  848. // Skip past any whitespace at the beginning.
  849. if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
  850. {
  851. do
  852. {
  853. index++;
  854. if ((uint)index >= (uint)value.Length)
  855. goto FalseExit;
  856. num = value[index];
  857. }
  858. while (IsWhite(num));
  859. }
  860. // Parse leading sign.
  861. bool overflow = false;
  862. if ((styles & NumberStyles.AllowLeadingSign) != 0)
  863. {
  864. if (info.HasInvariantNumberSigns)
  865. {
  866. if (num == '+')
  867. {
  868. index++;
  869. if ((uint)index >= (uint)value.Length)
  870. goto FalseExit;
  871. num = value[index];
  872. }
  873. else if (num == '-')
  874. {
  875. overflow = true;
  876. index++;
  877. if ((uint)index >= (uint)value.Length)
  878. goto FalseExit;
  879. num = value[index];
  880. }
  881. }
  882. else
  883. {
  884. value = value.Slice(index);
  885. index = 0;
  886. string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
  887. if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
  888. {
  889. index += positiveSign.Length;
  890. if ((uint)index >= (uint)value.Length)
  891. goto FalseExit;
  892. num = value[index];
  893. }
  894. else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
  895. {
  896. overflow = true;
  897. index += negativeSign.Length;
  898. if ((uint)index >= (uint)value.Length)
  899. goto FalseExit;
  900. num = value[index];
  901. }
  902. }
  903. }
  904. int answer = 0;
  905. if (IsDigit(num))
  906. {
  907. // Skip past leading zeros.
  908. if (num == '0')
  909. {
  910. do
  911. {
  912. index++;
  913. if ((uint)index >= (uint)value.Length)
  914. goto DoneAtEnd;
  915. num = value[index];
  916. } while (num == '0');
  917. if (!IsDigit(num))
  918. goto HasTrailingCharsZero;
  919. }
  920. // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
  921. answer = num - '0'; // first digit
  922. index++;
  923. for (int i = 0; i < 8; i++) // next 8 digits can't overflow
  924. {
  925. if ((uint)index >= (uint)value.Length)
  926. goto DoneAtEndButPotentialOverflow;
  927. num = value[index];
  928. if (!IsDigit(num))
  929. goto HasTrailingChars;
  930. index++;
  931. answer = 10 * answer + num - '0';
  932. }
  933. if ((uint)index >= (uint)value.Length)
  934. goto DoneAtEndButPotentialOverflow;
  935. num = value[index];
  936. if (!IsDigit(num))
  937. goto HasTrailingChars;
  938. index++;
  939. // Potential overflow now processing the 10th digit.
  940. overflow |= (uint)answer > uint.MaxValue / 10 || ((uint)answer == uint.MaxValue / 10 && num > '5');
  941. answer = answer * 10 + num - '0';
  942. if ((uint)index >= (uint)value.Length)
  943. goto DoneAtEndButPotentialOverflow;
  944. // At this point, we're either overflowing or hitting a formatting error.
  945. // Format errors take precedence for compatibility.
  946. num = value[index];
  947. while (IsDigit(num))
  948. {
  949. overflow = true;
  950. index++;
  951. if ((uint)index >= (uint)value.Length)
  952. goto OverflowExit;
  953. num = value[index];
  954. }
  955. goto HasTrailingChars;
  956. }
  957. goto FalseExit;
  958. DoneAtEndButPotentialOverflow:
  959. if (overflow)
  960. {
  961. goto OverflowExit;
  962. }
  963. DoneAtEnd:
  964. result = (uint)answer;
  965. ParsingStatus status = ParsingStatus.OK;
  966. Exit:
  967. return status;
  968. FalseExit: // parsing failed
  969. result = 0;
  970. status = ParsingStatus.Failed;
  971. goto Exit;
  972. OverflowExit:
  973. result = 0;
  974. status = ParsingStatus.Overflow;
  975. goto Exit;
  976. HasTrailingCharsZero:
  977. overflow = false;
  978. HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
  979. // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
  980. if (IsWhite(num))
  981. {
  982. if ((styles & NumberStyles.AllowTrailingWhite) == 0)
  983. goto FalseExit;
  984. for (index++; index < value.Length; index++)
  985. {
  986. if (!IsWhite(value[index]))
  987. break;
  988. }
  989. if ((uint)index >= (uint)value.Length)
  990. goto DoneAtEndButPotentialOverflow;
  991. }
  992. if (!TrailingZeros(value, index))
  993. goto FalseExit;
  994. goto DoneAtEndButPotentialOverflow;
  995. }
  996. /// <summary>Parses uint limited to styles that make up NumberStyles.HexNumber.</summary>
  997. private static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan<char> value, NumberStyles styles, out uint result)
  998. {
  999. Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
  1000. if (value.IsEmpty)
  1001. goto FalseExit;
  1002. int index = 0;
  1003. int num = value[0];
  1004. uint numValue;
  1005. // Skip past any whitespace at the beginning.
  1006. if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
  1007. {
  1008. do
  1009. {
  1010. index++;
  1011. if ((uint)index >= (uint)value.Length)
  1012. goto FalseExit;
  1013. num = value[index];
  1014. }
  1015. while (IsWhite(num));
  1016. }
  1017. bool overflow = false;
  1018. uint answer = 0;
  1019. ReadOnlySpan<byte> charToHexLookup = CharToHexLookup;
  1020. if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
  1021. {
  1022. // Skip past leading zeros.
  1023. if (num == '0')
  1024. {
  1025. do
  1026. {
  1027. index++;
  1028. if ((uint)index >= (uint)value.Length)
  1029. goto DoneAtEnd;
  1030. num = value[index];
  1031. } while (num == '0');
  1032. if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF)
  1033. goto HasTrailingChars;
  1034. }
  1035. // Parse up through 8 digits, as no overflow is possible
  1036. answer = charToHexLookup[num]; // first digit
  1037. index++;
  1038. for (int i = 0; i < 7; i++) // next 7 digits can't overflow
  1039. {
  1040. if ((uint)index >= (uint)value.Length)
  1041. goto DoneAtEnd;
  1042. num = value[index];
  1043. if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
  1044. goto HasTrailingChars;
  1045. index++;
  1046. answer = 16 * answer + numValue;
  1047. }
  1048. // If there's another digit, it's an overflow.
  1049. if ((uint)index >= (uint)value.Length)
  1050. goto DoneAtEnd;
  1051. num = value[index];
  1052. if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
  1053. goto HasTrailingChars;
  1054. // At this point, we're either overflowing or hitting a formatting error.
  1055. // Format errors take precedence for compatibility. Read through any remaining digits.
  1056. do
  1057. {
  1058. index++;
  1059. if ((uint)index >= (uint)value.Length)
  1060. goto OverflowExit;
  1061. num = value[index];
  1062. } while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF);
  1063. overflow = true;
  1064. goto HasTrailingChars;
  1065. }
  1066. goto FalseExit;
  1067. DoneAtEndButPotentialOverflow:
  1068. if (overflow)
  1069. {
  1070. goto OverflowExit;
  1071. }
  1072. DoneAtEnd:
  1073. result = answer;
  1074. ParsingStatus status = ParsingStatus.OK;
  1075. Exit:
  1076. return status;
  1077. FalseExit: // parsing failed
  1078. result = 0;
  1079. status = ParsingStatus.Failed;
  1080. goto Exit;
  1081. OverflowExit:
  1082. result = 0;
  1083. status = ParsingStatus.Overflow;
  1084. goto Exit;
  1085. HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
  1086. // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
  1087. if (IsWhite(num))
  1088. {
  1089. if ((styles & NumberStyles.AllowTrailingWhite) == 0)
  1090. goto FalseExit;
  1091. for (index++; index < value.Length; index++)
  1092. {
  1093. if (!IsWhite(value[index]))
  1094. break;
  1095. }
  1096. if ((uint)index >= (uint)value.Length)
  1097. goto DoneAtEndButPotentialOverflow;
  1098. }
  1099. if (!TrailingZeros(value, index))
  1100. goto FalseExit;
  1101. goto DoneAtEndButPotentialOverflow;
  1102. }
  1103. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1104. internal static ParsingStatus TryParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
  1105. {
  1106. if ((styles & ~NumberStyles.Integer) == 0)
  1107. {
  1108. // Optimized path for the common case of anything that's allowed for integer style.
  1109. return TryParseUInt64IntegerStyle(value, styles, info, out result);
  1110. }
  1111. if ((styles & NumberStyles.AllowHexSpecifier) != 0)
  1112. {
  1113. return TryParseUInt64HexNumberStyle(value, styles, out result);
  1114. }
  1115. return TryParseUInt64Number(value, styles, info, out result);
  1116. }
  1117. private static unsafe ParsingStatus TryParseUInt64Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
  1118. {
  1119. result = 0;
  1120. byte* pDigits = stackalloc byte[UInt64NumberBufferLength];
  1121. NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength);
  1122. if (!TryStringToNumber(value, styles, ref number, info))
  1123. {
  1124. return ParsingStatus.Failed;
  1125. }
  1126. if (!TryNumberToUInt64(ref number, ref result))
  1127. {
  1128. return ParsingStatus.Overflow;
  1129. }
  1130. return ParsingStatus.OK;
  1131. }
  1132. /// <summary>Parses ulong limited to styles that make up NumberStyles.Integer.</summary>
  1133. internal static ParsingStatus TryParseUInt64IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
  1134. {
  1135. Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
  1136. if (value.IsEmpty)
  1137. goto FalseExit;
  1138. int index = 0;
  1139. int num = value[0];
  1140. // Skip past any whitespace at the beginning.
  1141. if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
  1142. {
  1143. do
  1144. {
  1145. index++;
  1146. if ((uint)index >= (uint)value.Length)
  1147. goto FalseExit;
  1148. num = value[index];
  1149. }
  1150. while (IsWhite(num));
  1151. }
  1152. // Parse leading sign.
  1153. bool overflow = false;
  1154. if ((styles & NumberStyles.AllowLeadingSign) != 0)
  1155. {
  1156. if (info.HasInvariantNumberSigns)
  1157. {
  1158. if (num == '+')
  1159. {
  1160. index++;
  1161. if ((uint)index >= (uint)value.Length)
  1162. goto FalseExit;
  1163. num = value[index];
  1164. }
  1165. else if (num == '-')
  1166. {
  1167. overflow = true;
  1168. index++;
  1169. if ((uint)index >= (uint)value.Length)
  1170. goto FalseExit;
  1171. num = value[index];
  1172. }
  1173. }
  1174. else
  1175. {
  1176. value = value.Slice(index);
  1177. index = 0;
  1178. string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
  1179. if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
  1180. {
  1181. index += positiveSign.Length;
  1182. if ((uint)index >= (uint)value.Length)
  1183. goto FalseExit;
  1184. num = value[index];
  1185. }
  1186. else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
  1187. {
  1188. overflow = true;
  1189. index += negativeSign.Length;
  1190. if ((uint)index >= (uint)value.Length)
  1191. goto FalseExit;
  1192. num = value[index];
  1193. }
  1194. }
  1195. }
  1196. long answer = 0;
  1197. if (IsDigit(num))
  1198. {
  1199. // Skip past leading zeros.
  1200. if (num == '0')
  1201. {
  1202. do
  1203. {
  1204. index++;
  1205. if ((uint)index >= (uint)value.Length)
  1206. goto DoneAtEnd;
  1207. num = value[index];
  1208. } while (num == '0');
  1209. if (!IsDigit(num))
  1210. goto HasTrailingCharsZero;
  1211. }
  1212. // Parse most digits, up to the potential for overflow, which can't happen until after 19 digits.
  1213. answer = num - '0'; // first digit
  1214. index++;
  1215. for (int i = 0; i < 18; i++) // next 18 digits can't overflow
  1216. {
  1217. if ((uint)index >= (uint)value.Length)
  1218. goto DoneAtEndButPotentialOverflow;
  1219. num = value[index];
  1220. if (!IsDigit(num))
  1221. goto HasTrailingChars;
  1222. index++;
  1223. answer = 10 * answer + num - '0';
  1224. }
  1225. if ((uint)index >= (uint)value.Length)
  1226. goto DoneAtEndButPotentialOverflow;
  1227. num = value[index];
  1228. if (!IsDigit(num))
  1229. goto HasTrailingChars;
  1230. index++;
  1231. // Potential overflow now processing the 20th digit.
  1232. overflow |= (ulong)answer > ulong.MaxValue / 10 || ((ulong)answer == ulong.MaxValue / 10 && num > '5');
  1233. answer = answer * 10 + num - '0';
  1234. if ((uint)index >= (uint)value.Length)
  1235. goto DoneAtEndButPotentialOverflow;
  1236. // At this point, we're either overflowing or hitting a formatting error.
  1237. // Format errors take precedence for compatibility.
  1238. num = value[index];
  1239. while (IsDigit(num))
  1240. {
  1241. overflow = true;
  1242. index++;
  1243. if ((uint)index >= (uint)value.Length)
  1244. goto OverflowExit;
  1245. num = value[index];
  1246. }
  1247. goto HasTrailingChars;
  1248. }
  1249. goto FalseExit;
  1250. DoneAtEndButPotentialOverflow:
  1251. if (overflow)
  1252. {
  1253. goto OverflowExit;
  1254. }
  1255. DoneAtEnd:
  1256. result = (ulong)answer;
  1257. ParsingStatus status = ParsingStatus.OK;
  1258. Exit:
  1259. return status;
  1260. FalseExit: // parsing failed
  1261. result = 0;
  1262. status = ParsingStatus.Failed;
  1263. goto Exit;
  1264. OverflowExit:
  1265. result = 0;
  1266. status = ParsingStatus.Overflow;
  1267. goto Exit;
  1268. HasTrailingCharsZero:
  1269. overflow = false;
  1270. HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
  1271. // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
  1272. if (IsWhite(num))
  1273. {
  1274. if ((styles & NumberStyles.AllowTrailingWhite) == 0)
  1275. goto FalseExit;
  1276. for (index++; index < value.Length; index++)
  1277. {
  1278. if (!IsWhite(value[index]))
  1279. break;
  1280. }
  1281. if ((uint)index >= (uint)value.Length)
  1282. goto DoneAtEndButPotentialOverflow;
  1283. }
  1284. if (!TrailingZeros(value, index))
  1285. goto FalseExit;
  1286. goto DoneAtEndButPotentialOverflow;
  1287. }
  1288. /// <summary>Parses ulong limited to styles that make up NumberStyles.HexNumber.</summary>
  1289. private static ParsingStatus TryParseUInt64HexNumberStyle(ReadOnlySpan<char> value, NumberStyles styles, out ulong result)
  1290. {
  1291. Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
  1292. if (value.IsEmpty)
  1293. goto FalseExit;
  1294. int index = 0;
  1295. int num = value[0];
  1296. uint numValue;
  1297. // Skip past any whitespace at the beginning.
  1298. if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
  1299. {
  1300. do
  1301. {
  1302. index++;
  1303. if ((uint)index >= (uint)value.Length)
  1304. goto FalseExit;
  1305. num = value[index];
  1306. }
  1307. while (IsWhite(num));
  1308. }
  1309. bool overflow = false;
  1310. ulong answer = 0;
  1311. ReadOnlySpan<byte> charToHexLookup = CharToHexLookup;
  1312. if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
  1313. {
  1314. // Skip past leading zeros.
  1315. if (num == '0')
  1316. {
  1317. do
  1318. {
  1319. index++;
  1320. if ((uint)index >= (uint)value.Length)
  1321. goto DoneAtEnd;
  1322. num = value[index];
  1323. } while (num == '0');
  1324. if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF)
  1325. goto HasTrailingChars;
  1326. }
  1327. // Parse up through 16 digits, as no overflow is possible
  1328. answer = charToHexLookup[num]; // first digit
  1329. index++;
  1330. for (int i = 0; i < 15; i++) // next 15 digits can't overflow
  1331. {
  1332. if ((uint)index >= (uint)value.Length)
  1333. goto DoneAtEnd;
  1334. num = value[index];
  1335. if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
  1336. goto HasTrailingChars;
  1337. index++;
  1338. answer = 16 * answer + numValue;
  1339. }
  1340. // If there's another digit, it's an overflow.
  1341. if ((uint)index >= (uint)value.Length)
  1342. goto DoneAtEnd;
  1343. num = value[index];
  1344. if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
  1345. goto HasTrailingChars;
  1346. // At this point, we're either overflowing or hitting a formatting error.
  1347. // Format errors take precedence for compatibility. Read through any remaining digits.
  1348. do
  1349. {
  1350. index++;
  1351. if ((uint)index >= (uint)value.Length)
  1352. goto OverflowExit;
  1353. num = value[index];
  1354. } while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF);
  1355. overflow = true;
  1356. goto HasTrailingChars;
  1357. }
  1358. goto FalseExit;
  1359. DoneAtEndButPotentialOverflow:
  1360. if (overflow)
  1361. {
  1362. goto OverflowExit;
  1363. }
  1364. DoneAtEnd:
  1365. result = answer;
  1366. ParsingStatus status = ParsingStatus.OK;
  1367. Exit:
  1368. return status;
  1369. FalseExit: // parsing failed
  1370. result = 0;
  1371. status = ParsingStatus.Failed;
  1372. goto Exit;
  1373. OverflowExit:
  1374. result = 0;
  1375. status = ParsingStatus.Overflow;
  1376. goto Exit;
  1377. HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
  1378. // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
  1379. if (IsWhite(num))
  1380. {
  1381. if ((styles & NumberStyles.AllowTrailingWhite) == 0)
  1382. goto FalseExit;
  1383. for (index++; index < value.Length; index++)
  1384. {
  1385. if (!IsWhite(value[index]))
  1386. break;
  1387. }
  1388. if ((uint)index >= (uint)value.Length)
  1389. goto DoneAtEndButPotentialOverflow;
  1390. }
  1391. if (!TrailingZeros(value, index))
  1392. goto FalseExit;
  1393. goto DoneAtEndButPotentialOverflow;
  1394. }
  1395. internal static decimal ParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
  1396. {
  1397. ParsingStatus status = TryParseDecimal(value, styles, info, out decimal result);
  1398. if (status != ParsingStatus.OK)
  1399. {
  1400. ThrowOverflowOrFormatException(status, TypeCode.Decimal);
  1401. }
  1402. return result;
  1403. }
  1404. internal static unsafe bool TryNumberToDecimal(ref NumberBuffer number, ref decimal value)
  1405. {
  1406. number.CheckConsistency();
  1407. byte* p = number.GetDigitsPointer();
  1408. int e = number.Scale;
  1409. bool sign = number.IsNegative;
  1410. uint c = *p;
  1411. if (c == 0)
  1412. {
  1413. // To avoid risking an app-compat issue with pre 4.5 (where some app was illegally using Reflection to examine the internal scale bits), we'll only force
  1414. // the scale to 0 if the scale was previously positive (previously, such cases were unparsable to a bug.)
  1415. value = new decimal(0, 0, 0, sign, (byte)Math.Clamp(-e, 0, 28));
  1416. return true;
  1417. }
  1418. if (e > DecimalPrecision)
  1419. return false;
  1420. ulong low64 = 0;
  1421. while (e > -28)
  1422. {
  1423. e--;
  1424. low64 *= 10;
  1425. low64 += c - '0';
  1426. c = *++p;
  1427. if (low64 >= ulong.MaxValue / 10)
  1428. break;
  1429. if (c == 0)
  1430. {
  1431. while (e > 0)
  1432. {
  1433. e--;
  1434. low64 *= 10;
  1435. if (low64 >= ulong.MaxValue / 10)
  1436. break;
  1437. }
  1438. break;
  1439. }
  1440. }
  1441. uint high = 0;
  1442. while ((e > 0 || (c != 0 && e > -28)) &&
  1443. (high < uint.MaxValue / 10 || (high == uint.MaxValue / 10 && (low64 < 0x99999999_99999999 || (low64 == 0x99999999_99999999 && c <= '5')))))
  1444. {
  1445. // multiply by 10
  1446. ulong tmpLow = (uint)low64 * 10UL;
  1447. ulong tmp64 = (uint)(low64 >> 32) * 10UL + (tmpLow >> 32);
  1448. low64 = (uint)tmpLow + (tmp64 << 32);
  1449. high = (uint)(tmp64 >> 32) + high * 10;
  1450. if (c != 0)
  1451. {
  1452. c -= '0';
  1453. low64 += c;
  1454. if (low64 < c)
  1455. high++;
  1456. c = *++p;
  1457. }
  1458. e--;
  1459. }
  1460. if (c >= '5')
  1461. {
  1462. if ((c == '5') && ((low64 & 1) == 0))
  1463. {
  1464. c = *++p;
  1465. bool hasZeroTail = !number.HasNonZeroTail;
  1466. // We might still have some additional digits, in which case they need
  1467. // to be considered as part of hasZeroTail. Some examples of this are:
  1468. // * 3.0500000000000000000001e-27
  1469. // * 3.05000000000000000000001e-27
  1470. // In these cases, we will have processed 3 and 0, and ended on 5. The
  1471. // buffer, however, will still contain a number of trailing zeros and
  1472. // a trailing non-zero number.
  1473. while ((c != 0) && hasZeroTail)
  1474. {
  1475. hasZeroTail &= (c == '0');
  1476. c = *++p;
  1477. }
  1478. // We should either be at the end of the stream or have a non-zero tail
  1479. Debug.Assert((c == 0) || !hasZeroTail);
  1480. if (hasZeroTail)
  1481. {
  1482. // When the next digit is 5, the number is even, and all following
  1483. // digits are zero we don't need to round.
  1484. goto NoRounding;
  1485. }
  1486. }
  1487. if (++low64 == 0 && ++high == 0)
  1488. {
  1489. low64 = 0x99999999_9999999A;
  1490. high = uint.MaxValue / 10;
  1491. e++;
  1492. }
  1493. }
  1494. NoRounding:
  1495. if (e > 0)
  1496. return false;
  1497. if (e <= -DecimalPrecision)
  1498. {
  1499. // Parsing a large scale zero can give you more precision than fits in the decimal.
  1500. // This should only happen for actual zeros or very small numbers that round to zero.
  1501. value = new decimal(0, 0, 0, sign, DecimalPrecision - 1);
  1502. }
  1503. else
  1504. {
  1505. value = new decimal((int)low64, (int)(low64 >> 32), (int)high, sign, (byte)-e);
  1506. }
  1507. return true;
  1508. }
  1509. internal static double ParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
  1510. {
  1511. if (!TryParseDouble(value, styles, info, out double result))
  1512. {
  1513. ThrowOverflowOrFormatException(ParsingStatus.Failed);
  1514. }
  1515. return result;
  1516. }
  1517. internal static float ParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
  1518. {
  1519. if (!TryParseSingle(value, styles, info, out float result))
  1520. {
  1521. ThrowOverflowOrFormatException(ParsingStatus.Failed);
  1522. }
  1523. return result;
  1524. }
  1525. internal static unsafe ParsingStatus TryParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out decimal result)
  1526. {
  1527. byte* pDigits = stackalloc byte[DecimalNumberBufferLength];
  1528. NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, pDigits, DecimalNumberBufferLength);
  1529. result = 0;
  1530. if (!TryStringToNumber(value, styles, ref number, info))
  1531. {
  1532. return ParsingStatus.Failed;
  1533. }
  1534. if (!TryNumberToDecimal(ref number, ref result))
  1535. {
  1536. return ParsingStatus.Overflow;
  1537. }
  1538. return ParsingStatus.OK;
  1539. }
  1540. internal static unsafe bool TryParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out double result)
  1541. {
  1542. byte* pDigits = stackalloc byte[DoubleNumberBufferLength];
  1543. NumberBuffer number = new NumberBuffer(NumberBufferKind.FloatingPoint, pDigits, DoubleNumberBufferLength);
  1544. if (!TryStringToNumber(value, styles, ref number, info))
  1545. {
  1546. ReadOnlySpan<char> valueTrim = value.Trim();
  1547. // This code would be simpler if we only had the concept of `InfinitySymbol`, but
  1548. // we don't so we'll check the existing cases first and then handle `PositiveSign` +
  1549. // `PositiveInfinitySymbol` and `PositiveSign/NegativeSign` + `NaNSymbol` last.
  1550. if (valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
  1551. {
  1552. result = double.PositiveInfinity;
  1553. }
  1554. else if (valueTrim.EqualsOrdinalIgnoreCase(info.NegativeInfinitySymbol))
  1555. {
  1556. result = double.NegativeInfinity;
  1557. }
  1558. else if (valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
  1559. {
  1560. result = double.NaN;
  1561. }
  1562. else if (valueTrim.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase))
  1563. {
  1564. valueTrim = valueTrim.Slice(info.PositiveSign.Length);
  1565. if (valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
  1566. {
  1567. result = double.PositiveInfinity;
  1568. }
  1569. else if (valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
  1570. {
  1571. result = double.NaN;
  1572. }
  1573. else
  1574. {
  1575. result = 0;
  1576. return false;
  1577. }
  1578. }
  1579. else if (valueTrim.StartsWith(info.NegativeSign, StringComparison.OrdinalIgnoreCase) &&
  1580. valueTrim.Slice(info.NegativeSign.Length).EqualsOrdinalIgnoreCase(info.NaNSymbol))
  1581. {
  1582. result = double.NaN;
  1583. }
  1584. else
  1585. {
  1586. result = 0;
  1587. return false; // We really failed
  1588. }
  1589. }
  1590. else
  1591. {
  1592. result = NumberToDouble(ref number);
  1593. }
  1594. return true;
  1595. }
  1596. internal static unsafe bool TryParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out float result)
  1597. {
  1598. byte* pDigits = stackalloc byte[SingleNumberBufferLength];
  1599. NumberBuffer number = new NumberBuffer(NumberBufferKind.FloatingPoint, pDigits, SingleNumberBufferLength);
  1600. if (!TryStringToNumber(value, styles, ref number, info))
  1601. {
  1602. ReadOnlySpan<char> valueTrim = value.Trim();
  1603. // This code would be simpler if we only had the concept of `InfinitySymbol`, but
  1604. // we don't so we'll check the existing cases first and then handle `PositiveSign` +
  1605. // `PositiveInfinitySymbol` and `PositiveSign/NegativeSign` + `NaNSymbol` last.
  1606. //
  1607. // Additionally, since some cultures ("wo") actually define `PositiveInfinitySymbol`
  1608. // to include `PositiveSign`, we need to check whether `PositiveInfinitySymbol` fits
  1609. // that case so that we don't start parsing things like `++infini`.
  1610. if (valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
  1611. {
  1612. result = float.PositiveInfinity;
  1613. }
  1614. else if (valueTrim.EqualsOrdinalIgnoreCase(info.NegativeInfinitySymbol))
  1615. {
  1616. result = float.NegativeInfinity;
  1617. }
  1618. else if (valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
  1619. {
  1620. result = float.NaN;
  1621. }
  1622. else if (valueTrim.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase))
  1623. {
  1624. valueTrim = valueTrim.Slice(info.PositiveSign.Length);
  1625. if (!info.PositiveInfinitySymbol.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase) && valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
  1626. {
  1627. result = float.PositiveInfinity;
  1628. }
  1629. else if (!info.NaNSymbol.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase) && valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
  1630. {
  1631. result = float.NaN;
  1632. }
  1633. else
  1634. {
  1635. result = 0;
  1636. return false;
  1637. }
  1638. }
  1639. else if (valueTrim.StartsWith(info.NegativeSign, StringComparison.OrdinalIgnoreCase) &&
  1640. !info.NaNSymbol.StartsWith(info.NegativeSign, StringComparison.OrdinalIgnoreCase) &&
  1641. valueTrim.Slice(info.NegativeSign.Length).EqualsOrdinalIgnoreCase(info.NaNSymbol))
  1642. {
  1643. result = float.NaN;
  1644. }
  1645. else
  1646. {
  1647. result = 0;
  1648. return false; // We really failed
  1649. }
  1650. }
  1651. else
  1652. {
  1653. result = NumberToSingle(ref number);
  1654. }
  1655. return true;
  1656. }
  1657. internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
  1658. {
  1659. Debug.Assert(info != null);
  1660. fixed (char* stringPointer = &MemoryMarshal.GetReference(value))
  1661. {
  1662. char* p = stringPointer;
  1663. if (!TryParseNumber(ref p, p + value.Length, styles, ref number, info)
  1664. || ((int)(p - stringPointer) < value.Length && !TrailingZeros(value, (int)(p - stringPointer))))
  1665. {
  1666. number.CheckConsistency();
  1667. return false;
  1668. }
  1669. }
  1670. number.CheckConsistency();
  1671. return true;
  1672. }
  1673. private static bool TrailingZeros(ReadOnlySpan<char> value, int index)
  1674. {
  1675. // For compatibility, we need to allow trailing zeros at the end of a number string
  1676. for (int i = index; (uint)i < (uint)value.Length; i++)
  1677. {
  1678. if (value[i] != '\0')
  1679. {
  1680. return false;
  1681. }
  1682. }
  1683. return true;
  1684. }
  1685. private static unsafe char* MatchChars(char* p, char* pEnd, string value)
  1686. {
  1687. Debug.Assert(p != null && pEnd != null && p <= pEnd && value != null);
  1688. fixed (char* stringPointer = value)
  1689. {
  1690. char* str = stringPointer;
  1691. if (*str != '\0')
  1692. {
  1693. // We only hurt the failure case
  1694. // This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 as a
  1695. // space character we use 0x20 space character instead to mean the same.
  1696. while (true)
  1697. {
  1698. char cp = p < pEnd ? *p : '\0';
  1699. if (cp != *str && !(*str == '\u00a0' && cp == '\u0020'))
  1700. {
  1701. break;
  1702. }
  1703. p++;
  1704. str++;
  1705. if (*str == '\0')
  1706. return p;
  1707. }
  1708. }
  1709. }
  1710. return null;
  1711. }
  1712. // Ternary op is a workaround for https://github.com/dotnet/coreclr/issues/914
  1713. private static bool IsWhite(int ch) => ch == 0x20 || (uint)(ch - 0x09) <= (0x0D - 0x09) ? true : false;
  1714. private static bool IsDigit(int ch) => ((uint)ch - '0') <= 9;
  1715. internal enum ParsingStatus
  1716. {
  1717. OK,
  1718. Failed,
  1719. Overflow
  1720. }
  1721. internal static void ThrowOverflowOrFormatException(ParsingStatus status, TypeCode type = 0) => throw GetException(status, type);
  1722. internal static void ThrowOverflowException(TypeCode type) => throw GetException(ParsingStatus.Overflow, type);
  1723. private static Exception GetException(ParsingStatus status, TypeCode type)
  1724. {
  1725. if (status == ParsingStatus.Failed)
  1726. return new FormatException(SR.Format_InvalidString);
  1727. string s;
  1728. switch (type)
  1729. {
  1730. case TypeCode.SByte:
  1731. s = SR.Overflow_SByte;
  1732. break;
  1733. case TypeCode.Byte:
  1734. s = SR.Overflow_Byte;
  1735. break;
  1736. case TypeCode.Int16:
  1737. s = SR.Overflow_Int16;
  1738. break;
  1739. case TypeCode.UInt16:
  1740. s = SR.Overflow_UInt16;
  1741. break;
  1742. case TypeCode.Int32:
  1743. s = SR.Overflow_Int32;
  1744. break;
  1745. case TypeCode.UInt32:
  1746. s = SR.Overflow_UInt32;
  1747. break;
  1748. case TypeCode.Int64:
  1749. s = SR.Overflow_Int64;
  1750. break;
  1751. case TypeCode.UInt64:
  1752. s = SR.Overflow_UInt64;
  1753. break;
  1754. default:
  1755. Debug.Assert(type == TypeCode.Decimal);
  1756. s = SR.Overflow_Decimal;
  1757. break;
  1758. }
  1759. return new OverflowException(s);
  1760. }
  1761. internal static double NumberToDouble(ref NumberBuffer number)
  1762. {
  1763. number.CheckConsistency();
  1764. ulong bits = NumberToFloatingPointBits(ref number, in FloatingPointInfo.Double);
  1765. double result = BitConverter.Int64BitsToDouble((long)(bits));
  1766. return number.IsNegative ? -result : result;
  1767. }
  1768. internal static float NumberToSingle(ref NumberBuffer number)
  1769. {
  1770. number.CheckConsistency();
  1771. uint bits = (uint)(NumberToFloatingPointBits(ref number, in FloatingPointInfo.Single));
  1772. float result = BitConverter.Int32BitsToSingle((int)(bits));
  1773. return number.IsNegative ? -result : result;
  1774. }
  1775. }
  1776. }