unsafe public void ParseNumberString_Works_Scnenarios() { Skip.If(base.NoDiffToolDetected(), "No diff tool detected"); Dictionary <string, string> sut = new Dictionary <string, string>(); sut.Add("leading zeros", "001"); sut.Add("leading zeros neg", "-001"); sut.Add("zero", "0"); sut.Add("double", "0.00000000000000212312312"); sut.Add("double neg", "-0.00000000000000212312312"); sut.Add("int", "1"); sut.Add("int neg", "-1"); sut.Add("autreint ", "123124"); sut.Add("autreint neg", "-123124"); sut.Add("notation scientifique", "4.56E+2"); sut.Add("notation scientifique neg", "-4.56E-2"); sut.Add("notation scientifique 2", "4.5644E+2"); sut.Add("notation scientifique 2 neg", "-4.5644E-2"); sut.Add("notation scientifique 3", "4424.5644E+22"); sut.Add("notation scientifique 3 neg", "-4424.5644E-22"); sut.Add("notation scientifique 4", "4424.5644E+223"); sut.Add("notation scientifique 4 neg", "-4424.5644E-223"); StringBuilder sb = new StringBuilder(); foreach (KeyValuePair <string, string> kv in sut) { sb.AppendLine($"Scenario : {kv.Key} "); sb.AppendLine($"Valeur : {kv.Value} "); fixed(char *p = kv.Value) { char *pend = p + kv.Value.Length; var res = ParsedNumberString.ParseNumberString(p, pend); sb.AppendLine($"Resultat : {res.exponent} {res.mantissa} {res.negative} {res.valid}"); sb.AppendLine(); } } // We do not want to fail the tests when the user has not // configured a diff tool. try { VerifyData(sb.ToString()); } catch (System.Exception ex) { Console.WriteLine(ex.Message); } }
unsafe public void ParseNumberString_Works_Scnenarios() { Dictionary <string, string> sut = new Dictionary <string, string>(); sut.Add("leading zeros", "001"); sut.Add("leading zeros neg", "-001"); sut.Add("zero", "0"); sut.Add("zero neg", "-0"); sut.Add("double", "0.00000000000000212312312"); sut.Add("double neg", "-0.00000000000000212312312"); sut.Add("int", "1"); sut.Add("int neg", "-1"); sut.Add("autreint ", "123124"); sut.Add("autreint neg", "-123124"); sut.Add("notation scientifique", "4.56E+2"); sut.Add("notation scientifique neg", "-4.56E-2"); sut.Add("notation scientifique 2", "4.5644E+2"); sut.Add("notation scientifique 2 neg", "-4.5644E-2"); sut.Add("notation scientifique 3", "4424.5644E+22"); sut.Add("notation scientifique 3 neg", "-4424.5644E-22"); sut.Add("notation scientifique 4", "4424.5644E+223"); sut.Add("notation scientifique 4 neg", "-4424.5644E-223"); StringBuilder sb = new StringBuilder(); foreach (KeyValuePair <string, string> kv in sut) { sb.AppendLine($"Scenario : {kv.Key} "); sb.AppendLine($"Valeur : {kv.Value} "); fixed(char *p = kv.Value) { char *pend = p + kv.Value.Length; var res = ParsedNumberString.ParseNumberString(p, pend); sb.AppendLine($"Resultat : {res.exponent} {res.mantissa} {res.negative} {res.valid}"); sb.AppendLine(); } } VerifyData(sb.ToString()); }
// SKIP [Benchmark(Description = "ParseNumberString() only")] public double FastParser_PNS() { double max = double.MinValue; foreach (string l in _lines) { unsafe { fixed(char *p = l) { var pni = ParsedNumberString.ParseNumberString(p, p + l.Length); max = pni.exponent > max ? pni.exponent : max; } } } return(max); }
public static float FastPath(ParsedNumberString pns) { float value = (float)pns.mantissa; if (pns.exponent < 0) { value = value / exact_power_of_ten(-pns.exponent); } else { value = value * exact_power_of_ten(pns.exponent); } if (pns.negative) { value = -value; } return(value); }
unsafe static internal float ParseNumber(char *first, char *last, chars_format expectedFormat = chars_format.is_general, char decimal_separator = '.') { while ((first != last) && Utils.is_space((byte)(*first))) { first++; } if (first == last) { throw new ArgumentException(); } ParsedNumberString pns = ParseNumberString(first, last, expectedFormat); if (!pns.valid) { return(HandleInvalidInput(first, last)); } // Next is Clinger's fast path. if (FloatBinaryConstants.min_exponent_fast_path <= pns.exponent && pns.exponent <= FloatBinaryConstants.max_exponent_fast_path && pns.mantissa <= FloatBinaryConstants.max_mantissa_fast_path && !pns.too_many_digits) { return(FastPath(pns)); } AdjustedMantissa am = ComputeFloat(pns.exponent, pns.mantissa); if (pns.too_many_digits) { if (am != ComputeFloat(pns.exponent, pns.mantissa + 1)) { am.power2 = -1; // value is invalid. } } // If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0), // then we need to go the long way around again. This is very uncommon. if (am.power2 < 0) { am = ParseLongMantissa(first, last, decimal_separator); } return(ToFloat(pns.negative, am)); }
unsafe static internal ParsedNumberString ParseNumberString(char *p, char *pend, chars_format expectedFormat = chars_format.is_general, char decimal_separator = '.') { ParsedNumberString answer = new ParsedNumberString(); answer.valid = false; answer.too_many_digits = false; answer.negative = (*p == '-'); if ((*p == '-') || (*p == '+')) { ++p; if (p == pend) { return(answer); } if (!Utils.is_integer(*p) && (*p != decimal_separator)) // culture info ? { // a sign must be followed by an integer or the dot return(answer); } } char *start_digits = p; ulong i = 0; // an unsigned int avoids signed overflows (which are bad) while ((p != pend) && Utils.is_integer(*p)) { // a multiplication by 10 is cheaper than an arbitrary integer // multiplication i = 10 * i + (ulong)(*p - '0'); // might overflow, we will handle the overflow later ++p; } char *end_of_integer_part = p; long digit_count = (long)(end_of_integer_part - start_digits); long exponent = 0; if ((p != pend) && (*p == decimal_separator)) { ++p; while ((p != pend) && Utils.is_integer(*p)) { byte digit = (byte)(*p - '0'); ++p; i = i * 10 + digit; // in rare cases, this will overflow, but that's ok } exponent = end_of_integer_part + 1 - p; digit_count -= exponent; } // we must have encountered at least one integer! if (digit_count == 0) { return(answer); } long exp_number = 0; // explicit exponential part if (expectedFormat.HasFlag(chars_format.is_scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) { char *location_of_e = p; ++p; bool neg_exp = false; if ((p != pend) && ('-' == *p)) { neg_exp = true; ++p; } else if ((p != pend) && ('+' == *p)) { ++p; } if ((p == pend) || !Utils.is_integer(*p)) { if (expectedFormat != chars_format.is_fixed) { // We are in error. return(answer); } // Otherwise, we will be ignoring the 'e'. p = location_of_e; } else { while ((p != pend) && Utils.is_integer(*p)) { byte digit = (byte)(*p - '0'); if (exp_number < 0x10000) { exp_number = 10 * exp_number + digit; } ++p; } if (neg_exp) { exp_number = -exp_number; } exponent += exp_number; } } else { // If it scientific and not fixed, we have to bail out. if ((expectedFormat.HasFlag(chars_format.is_scientific)) && !(expectedFormat.HasFlag(chars_format.is_fixed))) { return(answer); } } //answer.lastmatch = p; answer.valid = true; // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon. // // We can deal with up to 19 digits. if (digit_count > 19) { // this is uncommon // It is possible that the integer had an overflow. // We have to handle the case where we have 0.0000somenumber. // We need to be mindful of the case where we only have zeroes... // E.g., 0.000000000...000. char *start = start_digits; while ((start != pend) && (*start == '0' || *start == decimal_separator)) { if (*start == '0') { digit_count--; } start++; } if (digit_count > 19) { answer.too_many_digits = true; // Let us start again, this time, avoiding overflows. i = 0; p = start_digits; const ulong minimal_nineteen_digit_integer = 1000000000000000000; while ((i < minimal_nineteen_digit_integer) && (p != pend) && Utils.is_integer(*p)) { i = i * 10 + (ulong)(*p - '0'); ++p; } if (i >= minimal_nineteen_digit_integer) { // We have a big integers exponent = end_of_integer_part - p + exp_number; } else { // We have a value with a fractional component. p++; // skip the '.' char *first_after_period = p; while ((i < minimal_nineteen_digit_integer) && (p != pend) && Utils.is_integer(*p)) { i = i * 10 + (ulong)(*p - '0'); ++p; } exponent = first_after_period - p + exp_number; } // We have now corrected both exponent and i, to a truncated value } } answer.exponent = exponent; answer.mantissa = i; return(answer); }