From 20ecba5eb722b067738ec7e80b7df7a496aaa9f9 Mon Sep 17 00:00:00 2001 From: Florian Loitsch Date: Sun, 9 Sep 2018 02:48:27 +0200 Subject: [PATCH] Support separator characters. Fixes #49 --- double-conversion/double-conversion.cc | 124 +++++---- double-conversion/double-conversion.h | 23 +- test/cctest/test-conversions.cc | 352 ++++++++++++++++++++++++- 3 files changed, 444 insertions(+), 55 deletions(-) diff --git a/double-conversion/double-conversion.cc b/double-conversion/double-conversion.cc index ba83beff..ecd1a5ef 100644 --- a/double-conversion/double-conversion.cc +++ b/double-conversion/double-conversion.cc @@ -551,6 +551,26 @@ static bool IsCharacterDigitForRadix(int c, int radix, char a_character) { return radix > 10 && c >= a_character && c < a_character + radix - 10; } +// Returns true, when the iterator is equal to end. +template +static bool Advance (Iterator* it, char separator, int base, Iterator& end) { + if (separator == StringToDoubleConverter::kNoSeparator) { + ++(*it); + return *it == end; + } + if (!isDigit(**it, base)) { + ++(*it); + return *it == end; + } + ++(*it); + if (*it == end) return true; + if (*it + 1 == end) return false; + if (**it == separator && isDigit(*(*it + 1), base)) { + ++(*it); + } + return *it == end; +} + // Checks whether the string in the range start-end is a hex-float string. // This function assumes that the leading '0x'/'0X' is already consumed. // @@ -561,27 +581,35 @@ static bool IsCharacterDigitForRadix(int c, int radix, char a_character) { template static bool IsHexFloatString(Iterator start, Iterator end, + char separator, bool allow_trailing_junk) { ASSERT(start != end); Iterator current = start; - while (current != end && isDigit(*current, 16)) ++current; - if (current == end) return false; + bool saw_digit = false; + while (isDigit(*current, 16)) { + saw_digit = true; + if (Advance(¤t, separator, 16, end)) return false; + } if (*current == '.') { - ++current; - while (current != end && isDigit(*current, 16)) ++current; - if (current - start == 1) return false; // Only the '.', but no digits. + if (Advance(¤t, separator, 16, end)) return false; + while (isDigit(*current, 16)) { + saw_digit = true; + if (Advance(¤t, separator, 16, end)) return false; + } + if (!saw_digit) return false; // Only the '.', but no digits. } - if (current == end) return false; if (*current != 'p' && *current != 'P') return false; - ++current; - if (current == end) return false; - if (*current == '+' || *current == '-') ++current; - if (current == end) return false; + if (Advance(¤t, separator, 16, end)) return false; + if (*current == '+' || *current == '-') { + if (Advance(¤t, separator, 16, end)) return false; + } if (!isDigit(*current, 10)) return false; - ++current; - while (current != end && isDigit(*current, 10)) ++current; + if (Advance(¤t, separator, 16, end)) return true; + while (isDigit(*current, 10)) { + if (Advance(¤t, separator, 16, end)) return true; + } return allow_trailing_junk || !AdvanceToNonspace(¤t, end); } @@ -594,6 +622,7 @@ template static double RadixStringToIeee(Iterator* current, Iterator end, bool sign, + char separator, bool parse_as_hex_float, bool allow_trailing_junk, double junk_string_value, @@ -601,7 +630,7 @@ static double RadixStringToIeee(Iterator* current, bool* result_is_junk) { ASSERT(*current != end); ASSERT(!parse_as_hex_float || - IsHexFloatString(*current, end, allow_trailing_junk)); + IsHexFloatString(*current, end, separator, allow_trailing_junk)); const int kDoubleSize = Double::kSignificandSize; const int kSingleSize = Single::kSignificandSize; @@ -609,15 +638,6 @@ static double RadixStringToIeee(Iterator* current, *result_is_junk = true; - // Skip leading 0s. - while (**current == '0') { - ++(*current); - if (*current == end) { - *result_is_junk = false; - return SignedZero(sign); - } - } - int64_t number = 0; int exponent = 0; const int radix = (1 << radix_log_2); @@ -625,7 +645,15 @@ static double RadixStringToIeee(Iterator* current, // Only relevant if parse_as_hex_float is true. bool post_decimal = false; - do { + // Skip leading 0s. + while (**current == '0') { + if (Advance(current, separator, radix, end)) { + *result_is_junk = false; + return SignedZero(sign); + } + } + + while (true) { int digit; if (IsDecimalDigitForRadix(**current, radix)) { digit = static_cast(**current) - '0'; @@ -638,7 +666,8 @@ static double RadixStringToIeee(Iterator* current, if (post_decimal) exponent -= radix_log_2; } else if (parse_as_hex_float && **current == '.') { post_decimal = true; - ++(*current); + Advance(current, separator, radix, end); + ASSERT(*current != end); continue; } else if (parse_as_hex_float && (**current == 'p' || **current == 'P')) { break; @@ -668,14 +697,15 @@ static double RadixStringToIeee(Iterator* current, bool zero_tail = true; for (;;) { - ++(*current); + if (Advance(current, separator, radix, end)) break; if (parse_as_hex_float && **current == '.') { // Just run over the '.'. We are just trying to see whether there is // a non-zero digit somewhere. - ++(*current); + Advance(current, separator, radix, end); + ASSERT(*current != end); post_decimal = true; } - if (*current == end || !isDigit(**current, radix)) break; + if (!isDigit(**current, radix)) break; zero_tail = zero_tail && **current == '0'; if (!post_decimal) exponent += radix_log_2; } @@ -704,8 +734,8 @@ static double RadixStringToIeee(Iterator* current, } break; } - ++(*current); - } while (*current != end); + if (Advance(current, separator, radix, end)) break; + } ASSERT(number < ((int64_t)1 << kSignificandSize)); ASSERT(static_cast(static_cast(number)) == number); @@ -714,18 +744,21 @@ static double RadixStringToIeee(Iterator* current, if (parse_as_hex_float) { ASSERT(**current == 'p' || **current == 'P'); - ++(*current); + Advance(current, separator, radix, end); + ASSERT(*current != end); bool is_negative = false; if (**current == '+') { - ++(*current); + Advance(current, separator, radix, end); + ASSERT(*current != end); } else if (**current == '-') { is_negative = true; - ++(*current); + Advance(current, separator, radix, end); + ASSERT(*current != end); } int written_exponent = 0; - while (*current != end && IsDecimalDigitForRadix(**current, 10)) { + while (IsDecimalDigitForRadix(**current, 10)) { written_exponent = 10 * written_exponent + **current - '0'; - ++(*current); + if (Advance(current, separator, radix, end)) break; } if (is_negative) written_exponent = -written_exponent; exponent += written_exponent; @@ -761,7 +794,6 @@ double StringToDoubleConverter::StringToIeee( const bool allow_spaces_after_sign = (flags_ & ALLOW_SPACES_AFTER_SIGN) != 0; const bool allow_case_insensibility = (flags_ & ALLOW_CASE_INSENSIBILITY) != 0; - // To make sure that iterator dereferencing is valid the following // convention is used: // 1. Each '++current' statement is followed by check for equality to 'end'. @@ -849,8 +881,7 @@ double StringToDoubleConverter::StringToIeee( bool leading_zero = false; if (*current == '0') { - ++current; - if (current == end) { + if (Advance(¤t, separator_, 10, end)) { *processed_characters_count = static_cast(current - input); return SignedZero(sign); } @@ -863,7 +894,7 @@ double StringToDoubleConverter::StringToIeee( ++current; bool parse_as_hex_float = (flags_ & ALLOW_HEX_FLOATS) && - IsHexFloatString(current, end, allow_trailing_junk); + IsHexFloatString(current, end, separator_, allow_trailing_junk); if (current == end) return junk_string_value_; // "0x" if (!parse_as_hex_float && !isDigit(*current, 16)) { @@ -874,6 +905,7 @@ double StringToDoubleConverter::StringToIeee( double result = RadixStringToIeee<4>(¤t, end, sign, + separator_, parse_as_hex_float, allow_trailing_junk, junk_string_value_, @@ -888,8 +920,7 @@ double StringToDoubleConverter::StringToIeee( // Ignore leading zeros in the integer part. while (*current == '0') { - ++current; - if (current == end) { + if (Advance(¤t, separator_, 10, end)) { *processed_characters_count = static_cast(current - input); return SignedZero(sign); } @@ -910,8 +941,7 @@ double StringToDoubleConverter::StringToIeee( nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; } octal = octal && *current < '8'; - ++current; - if (current == end) goto parsing_done; + if (Advance(¤t, separator_, 10, end)) goto parsing_done; } if (significant_digits == 0) { @@ -922,8 +952,7 @@ double StringToDoubleConverter::StringToIeee( if (octal && !allow_trailing_junk) return junk_string_value_; if (octal) goto parsing_done; - ++current; - if (current == end) { + if (Advance(¤t, separator_, 10, end)) { if (significant_digits == 0 && !leading_zero) { return junk_string_value_; } else { @@ -936,8 +965,7 @@ double StringToDoubleConverter::StringToIeee( // Integer part consists of 0 or is absent. Significant digits start after // leading zeros (if any). while (*current == '0') { - ++current; - if (current == end) { + if (Advance(¤t, separator_, 10, end)) { *processed_characters_count = static_cast(current - input); return SignedZero(sign); } @@ -957,8 +985,7 @@ double StringToDoubleConverter::StringToIeee( // Ignore insignificant digits in the fractional part. nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; } - ++current; - if (current == end) goto parsing_done; + if (Advance(¤t, separator_, 10, end)) goto parsing_done; } } @@ -1045,6 +1072,7 @@ double StringToDoubleConverter::StringToIeee( result = RadixStringToIeee<3>(&start, buffer + buffer_pos, sign, + separator_, false, // Don't parse as hex_float. allow_trailing_junk, junk_string_value_, diff --git a/double-conversion/double-conversion.h b/double-conversion/double-conversion.h index 86cf25ea..7495d17a 100644 --- a/double-conversion/double-conversion.h +++ b/double-conversion/double-conversion.h @@ -399,6 +399,8 @@ class StringToDoubleConverter { ALLOW_HEX_FLOATS = 128, }; + static const uc16 kNoSeparator = '\0'; + // Flags should be a bit-or combination of the possible Flags-enum. // - NO_FLAGS: no special flags. // - ALLOW_HEX: recognizes the prefix "0x". Hex numbers may only be integers. @@ -459,6 +461,12 @@ class StringToDoubleConverter { // - they must not have the same first character. // - they must not start with digits. // + // If the separator character is not kNoSeparator, then that specific + // character is ignored when in between two valid digits of the significant. + // It is not allowed to appear in the exponent. + // It is not allowed to lead or trail the number. + // It is not allowed to appear twice next to each other. + // // Examples: // flags = ALLOW_HEX | ALLOW_TRAILING_JUNK, // empty_string_value = 0.0, @@ -498,16 +506,26 @@ class StringToDoubleConverter { // StringToDouble("01239E45") -> 1239e45. // StringToDouble("-infinity") -> NaN // junk_string_value. // StringToDouble("NaN") -> NaN // junk_string_value. + // + // flags = NO_FLAGS, + // separator = ' ': + // StringToDouble("1 2 3 4") -> 1234.0 + // StringToDouble("1 2") -> NaN // junk_string_value + // StringToDouble("1 000 000.0") -> 1000000.0 + // StringToDouble("1.000 000") -> 1.0 + // StringToDouble("1.0e1 000") -> NaN // junk_string_value StringToDoubleConverter(int flags, double empty_string_value, double junk_string_value, const char* infinity_symbol, - const char* nan_symbol) + const char* nan_symbol, + uc16 separator = kNoSeparator) : flags_(flags), empty_string_value_(empty_string_value), junk_string_value_(junk_string_value), infinity_symbol_(infinity_symbol), - nan_symbol_(nan_symbol) { + nan_symbol_(nan_symbol), + separator_(separator) { } // Performs the conversion. @@ -542,6 +560,7 @@ class StringToDoubleConverter { const double junk_string_value_; const char* const infinity_symbol_; const char* const nan_symbol_; + const uc16 separator_; template double StringToIeee(Iterator start_pointer, diff --git a/test/cctest/test-conversions.cc b/test/cctest/test-conversions.cc index cf556e6c..1173416f 100644 --- a/test/cctest/test-conversions.cc +++ b/test/cctest/test-conversions.cc @@ -1720,9 +1720,10 @@ TEST(DoubleToStringJavaScript) { static double StrToD16(const uc16* str16, int length, int flags, double empty_string_value, - int* processed_characters_count, bool* processed_all) { + int* processed_characters_count, bool* processed_all, + uc16 separator = StringToDoubleConverter::kNoSeparator) { StringToDoubleConverter converter(flags, empty_string_value, Double::NaN(), - NULL, NULL); + NULL, NULL, separator); double result = converter.StringToDouble(str16, length, processed_characters_count); *processed_all = (length == *processed_characters_count); @@ -1731,9 +1732,10 @@ static double StrToD16(const uc16* str16, int length, int flags, static double StrToD(const char* str, int flags, double empty_string_value, - int* processed_characters_count, bool* processed_all) { + int* processed_characters_count, bool* processed_all, + uc16 separator = StringToDoubleConverter::kNoSeparator) { StringToDoubleConverter converter(flags, empty_string_value, Double::NaN(), - NULL, NULL); + NULL, NULL, separator); double result = converter.StringToDouble(str, strlen(str), processed_characters_count); *processed_all = @@ -1748,7 +1750,8 @@ static double StrToD(const char* str, int flags, double empty_string_value, int processed_characters_count16; bool processed_all16; double result16 = StrToD16(buffer16, len, flags, empty_string_value, - &processed_characters_count16, &processed_all16); + &processed_characters_count16, &processed_all16, + separator); CHECK_EQ(result, result16); CHECK_EQ(*processed_characters_count, processed_characters_count16); return result; @@ -3196,6 +3199,323 @@ TEST(StringToDoubleOctalString) { } +TEST(StringToDoubleSeparator) { + int flags; + int processed; + bool all_used; + char separator; + + separator = '\''; + flags = StringToDoubleConverter::NO_FLAGS; + + CHECK_EQ(1.0, StrToD("000'001.0'0", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(1.0, StrToD("0'0'0'0'0'1.0'0", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(Double::NaN(), StrToD("'1.0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1'.0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.'0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("0''1.0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e1'0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e1'", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e'1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0'e1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("+'1.0e1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("-'1.0e1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e+'1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e-'1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e'+1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e'-1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + separator = ' '; + flags = StringToDoubleConverter::NO_FLAGS; + + CHECK_EQ(1.0, StrToD("000 001.0 0", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(1.0, StrToD("0 0 0 0 0 1.0 0", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(Double::NaN(), StrToD(" 1.0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1 .0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1. 0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("0 1.0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e1 0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e1 ", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e 1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0 e1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("+ 1.0e1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("- 1.0e1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e+ 1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e- 1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e +1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e -1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + separator = ' '; + flags = StringToDoubleConverter::ALLOW_LEADING_SPACES | + StringToDoubleConverter::ALLOW_TRAILING_SPACES; + + CHECK_EQ(1.0, StrToD("000 001.0 0", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(1.0, StrToD("0 0 0 0 0 1.0 0", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(1.0, StrToD(" 000 001.0 0 ", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(1.0, StrToD(" 0 0 0 0 0 1.0 0 ", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(1.0, StrToD(" 1.0", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(Double::NaN(), StrToD("1 .0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1. 0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("0 1.0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e1 0", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(10.0, StrToD("1.0e1 ", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(Double::NaN(), StrToD("1.0e 1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0 e1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("+ 1.0e1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("- 1.0e1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e+ 1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e- 1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e +1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("1.0e -1", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + separator = ' '; + flags = StringToDoubleConverter::ALLOW_HEX | + StringToDoubleConverter::ALLOW_HEX_FLOATS | + StringToDoubleConverter::ALLOW_LEADING_SPACES | + StringToDoubleConverter::ALLOW_TRAILING_SPACES; + + CHECK_EQ(18.0, StrToD("0x1 2", flags, 0.0, &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(0.0, StrToD("0x0 0", flags, 1.0, &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(static_cast(0x123456789), + StrToD("0x1 2 3 4 5 6 7 8 9", flags, Double::NaN(), + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(18.0, StrToD(" 0x1 2 ", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(0.0, StrToD(" 0x0 ", flags, 1.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(static_cast(0x123456789), + StrToD(" 0x1 2 3 4 5 6 7 8 9 ", flags, Double::NaN(), + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(static_cast(0xabcdef), + StrToD("0xa b c d e f", flags, 0.0, + &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(Double::NaN(), StrToD("0x 1 2", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("0 x0", flags, 1.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), + StrToD("0x1 2 3 4 5 6 7 8 9", flags, Double::NaN(), + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD(" 0 x1 2 ", flags, 0.0, + &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(3.0, + StrToD("0x0 3p0", flags, 0.0, &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(0.0, + StrToD("0x.0 0p0", flags, 0.0, &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(3.0, + StrToD("0x3.0 0p0", flags, 0.0, &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(3.0, + StrToD("0x0 3.p0", flags, 0.0, &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(Double::NaN(), + StrToD("0x 3p0", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), + StrToD("0x.0 p0", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), + StrToD("0x3.0p0 0", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), + StrToD("0x0 3.p 0", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), + StrToD("0x3p+ 0", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), + StrToD("0x.0p- 0", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), + StrToD("0x3.0p +0", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), + StrToD("0x0 3.p -0", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); +} + TEST(StringToDoubleSpecialValues) { int processed; int flags = StringToDoubleConverter::NO_FLAGS; @@ -3369,6 +3689,28 @@ TEST(StringToDoubleCommentExamples) { CHECK_EQ(Double::NaN(), StrToD("NaN", flags, 0.0, &processed, &all_used)); CHECK_EQ(0, processed); + + flags = StringToDoubleConverter::NO_FLAGS; + char separator = ' '; + CHECK_EQ(1234.0, + StrToD("1 2 3 4", flags, 0.0, &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(Double::NaN(), + StrToD("1 2", flags, 0.0, &processed, &all_used, separator)); + CHECK_EQ(0, processed); + + CHECK_EQ(1000000.0, + StrToD("1 000 000.0", flags, 0.0, &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(1.0, + StrToD("1.000 000", flags, 0.0, &processed, &all_used, separator)); + CHECK(all_used); + + CHECK_EQ(Double::NaN(), + StrToD("1.0e1 000", flags, 0.0, &processed, &all_used, separator)); + CHECK_EQ(0, processed); }