Add support for hexadecimal float literals.
Fixes #48.
diff --git a/double-conversion/double-conversion.cc b/double-conversion/double-conversion.cc
index 7c8e9c6..ba83bef 100644
--- a/double-conversion/double-conversion.cc
+++ b/double-conversion/double-conversion.cc
@@ -537,7 +537,7 @@
#pragma optimize("",on)
#else
static bool inline IsDecimalDigitForRadix(int c, int radix) {
- return '0' <= c && c <= '9' && (c - '0') < radix;
+ return '0' <= c && c <= '9' && (c - '0') < radix;
}
#endif
// Returns true if 'c' is a character digit that is valid for the given radix.
@@ -551,17 +551,57 @@
return radix > 10 && c >= a_character && c < a_character + radix - 10;
}
+// 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.
+//
+// Hex float strings are of one of the following forms:
+// - hex_digits+ 'p' ('+'|'-')? exponent_digits+
+// - hex_digits* '.' hex_digits+ 'p' ('+'|'-')? exponent_digits+
+// - hex_digits+ '.' 'p' ('+'|'-')? exponent_digits+
+template<class Iterator>
+static bool IsHexFloatString(Iterator start,
+ Iterator end,
+ bool allow_trailing_junk) {
+ ASSERT(start != end);
+
+ Iterator current = start;
+
+ while (current != end && isDigit(*current, 16)) ++current;
+ if (current == 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 (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 (!isDigit(*current, 10)) return false;
+ ++current;
+ while (current != end && isDigit(*current, 10)) ++current;
+ return allow_trailing_junk || !AdvanceToNonspace(¤t, end);
+}
+
// Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end.
+//
+// If parse_as_hex_float is true, then the string must be a valid
+// hex-float.
template <int radix_log_2, class Iterator>
static double RadixStringToIeee(Iterator* current,
Iterator end,
bool sign,
+ bool parse_as_hex_float,
bool allow_trailing_junk,
double junk_string_value,
bool read_as_double,
bool* result_is_junk) {
ASSERT(*current != end);
+ ASSERT(!parse_as_hex_float ||
+ IsHexFloatString(*current, end, allow_trailing_junk));
const int kDoubleSize = Double::kSignificandSize;
const int kSingleSize = Single::kSignificandSize;
@@ -581,15 +621,27 @@
int64_t number = 0;
int exponent = 0;
const int radix = (1 << radix_log_2);
+ // Whether we have encountered a '.' and are parsing the decimal digits.
+ // Only relevant if parse_as_hex_float is true.
+ bool post_decimal = false;
do {
int digit;
if (IsDecimalDigitForRadix(**current, radix)) {
digit = static_cast<char>(**current) - '0';
+ if (post_decimal) exponent -= radix_log_2;
} else if (IsCharacterDigitForRadix(**current, radix, 'a')) {
digit = static_cast<char>(**current) - 'a' + 10;
+ if (post_decimal) exponent -= radix_log_2;
} else if (IsCharacterDigitForRadix(**current, radix, 'A')) {
digit = static_cast<char>(**current) - 'A' + 10;
+ if (post_decimal) exponent -= radix_log_2;
+ } else if (parse_as_hex_float && **current == '.') {
+ post_decimal = true;
+ ++(*current);
+ continue;
+ } else if (parse_as_hex_float && (**current == 'p' || **current == 'P')) {
+ break;
} else {
if (allow_trailing_junk || !AdvanceToNonspace(current, end)) {
break;
@@ -612,17 +664,25 @@
int dropped_bits_mask = ((1 << overflow_bits_count) - 1);
int dropped_bits = static_cast<int>(number) & dropped_bits_mask;
number >>= overflow_bits_count;
- exponent = overflow_bits_count;
+ exponent += overflow_bits_count;
bool zero_tail = true;
for (;;) {
++(*current);
+ 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);
+ post_decimal = true;
+ }
if (*current == end || !isDigit(**current, radix)) break;
zero_tail = zero_tail && **current == '0';
- exponent += radix_log_2;
+ if (!post_decimal) exponent += radix_log_2;
}
- if (!allow_trailing_junk && AdvanceToNonspace(current, end)) {
+ if (!parse_as_hex_float &&
+ !allow_trailing_junk &&
+ AdvanceToNonspace(current, end)) {
return junk_string_value;
}
@@ -652,7 +712,26 @@
*result_is_junk = false;
- if (exponent == 0) {
+ if (parse_as_hex_float) {
+ ASSERT(**current == 'p' || **current == 'P');
+ ++(*current);
+ bool is_negative = false;
+ if (**current == '+') {
+ ++(*current);
+ } else if (**current == '-') {
+ is_negative = true;
+ ++(*current);
+ }
+ int written_exponent = 0;
+ while (*current != end && IsDecimalDigitForRadix(**current, 10)) {
+ written_exponent = 10 * written_exponent + **current - '0';
+ ++(*current);
+ }
+ if (is_negative) written_exponent = -written_exponent;
+ exponent += written_exponent;
+ }
+
+ if (exponent == 0 || number == 0) {
if (sign) {
if (number == 0) return -0.0;
number = -number;
@@ -779,16 +858,23 @@
leading_zero = true;
// It could be hexadecimal value.
- if ((flags_ & ALLOW_HEX) && (*current == 'x' || *current == 'X')) {
+ if (((flags_ & ALLOW_HEX) || (flags_ & ALLOW_HEX_FLOATS)) &&
+ (*current == 'x' || *current == 'X')) {
++current;
- if (current == end || !isDigit(*current, 16)) {
- return junk_string_value_; // "0x".
+
+ bool parse_as_hex_float = (flags_ & ALLOW_HEX_FLOATS) &&
+ IsHexFloatString(current, end, allow_trailing_junk);
+
+ if (current == end) return junk_string_value_; // "0x"
+ if (!parse_as_hex_float && !isDigit(*current, 16)) {
+ return junk_string_value_;
}
bool result_is_junk;
double result = RadixStringToIeee<4>(¤t,
end,
sign,
+ parse_as_hex_float,
allow_trailing_junk,
junk_string_value_,
read_as_double,
@@ -959,6 +1045,7 @@
result = RadixStringToIeee<3>(&start,
buffer + buffer_pos,
sign,
+ false, // Don't parse as hex_float.
allow_trailing_junk,
junk_string_value_,
read_as_double,
diff --git a/double-conversion/double-conversion.h b/double-conversion/double-conversion.h
index 1ccd7fc..86cf25e 100644
--- a/double-conversion/double-conversion.h
+++ b/double-conversion/double-conversion.h
@@ -396,6 +396,7 @@
ALLOW_TRAILING_SPACES = 16,
ALLOW_SPACES_AFTER_SIGN = 32,
ALLOW_CASE_INSENSIBILITY = 64,
+ ALLOW_HEX_FLOATS = 128,
};
// Flags should be a bit-or combination of the possible Flags-enum.
@@ -429,6 +430,11 @@
// StringToDouble("+ 123.2") -> 123.2
// - ALLOW_CASE_INSENSIBILITY: ignore case of characters for special values:
// infinity and nan.
+ // - ALLOW_HEX_FLOATS: allows hexadecimal float literals.
+ // This *must* start with "0x" and separate the exponent with "p".
+ // Examples: 0x1.2p3 == 9.0
+ // 0x10.1p0 == 16.0625
+ // ALLOW_HEX and ALLOW_HEX_FLOATS are indendent.
//
// empty_string_value is returned when an empty string is given as input.
// If ALLOW_LEADING_SPACES or ALLOW_TRAILING_SPACES are set, then a string
diff --git a/test/cctest/test-conversions.cc b/test/cctest/test-conversions.cc
index d70624d..cf556e6 100644
--- a/test/cctest/test-conversions.cc
+++ b/test/cctest/test-conversions.cc
@@ -2563,6 +2563,91 @@
CHECK_EQ(295147905179352960000.0, StrToD("0x100000000000018000", flags, 0.0,
&processed, &all_used));
CHECK(all_used);
+
+ flags = StringToDoubleConverter::ALLOW_HEX_FLOATS;
+
+ CHECK_EQ(3.0, StrToD("0x3p0", flags, 0.0, &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(0.0, StrToD("0x.0p0", flags, 0.0, &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(3.0, StrToD("0x3.0p0", flags, 0.0, &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(3.0, StrToD("0x3.p0", flags, 0.0, &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(-5.634002666912405e+27, StrToD("-0x123456789012345678901234p0",
+ flags, 0.0,
+ &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(72057594037927940.0, StrToD("0x100000000000001p0", flags, 0.0,
+ &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(72057594037927940.0, StrToD("0x100000000000000p0", flags, 0.0,
+ &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(295147905179352830000.0, StrToD("0x100000000000000001p0", flags, 0.0,
+ &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(295147905179352830000.0, StrToD("0x100000000000000000p0", flags, 0.0,
+ &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(295147905179352900000.0, StrToD("0x100000000000008001p0", flags, 0.0,
+ &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(295147905179352830000.0, StrToD("0x100000000000008000p0", flags, 0.0,
+ &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(295147905179352960000.0, StrToD("0x100000000000018001p0", flags, 0.0,
+ &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(295147905179352960000.0, StrToD("0x100000000000018000p0", flags, 0.0,
+ &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(4.722366482869645e+21, StrToD("0x100000000000000001p4", flags, 0.0,
+ &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(4.722366482869645e+21, StrToD("0x100000000000000000p+4", flags, 0.0,
+ &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(4.722366482869646e+21, StrToD("0x100000000000008001p04", flags, 0.0,
+ &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(18446744073709552000.0, StrToD("0x100000000000008000p-4", flags, 0.0,
+ &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(18446744073709560000.0, StrToD("0x100000000000018001p-04", flags, 0.0,
+ &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(4.722366482869647e+21, StrToD("0x100000000000018000p4", flags, 0.0,
+ &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(Double::Infinity(), StrToD("0x1p2000", flags, 0.0,
+ &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(0.0, StrToD("0x1p-2000", flags, 0.0, &processed, &all_used));
+ CHECK(all_used);
+
+ CHECK_EQ(-0.0, StrToD("-0x1p-2000", flags, 0.0, &processed, &all_used));
+ CHECK(all_used);
}
@@ -3789,13 +3874,30 @@
CHECK_EQ(5.0f, StrToF(" + 0x5 ", flags, 0.0f, &processed, &all_used));
CHECK(all_used);
- CHECK_EQ(Single::NaN(), StrToF("- -0x5", flags, 0.0f, &processed, &all_used));
+ CHECK_EQ(Single::NaN(), StrToF("- -0x5", flags, 0.0f,
+ &processed, &all_used));
CHECK_EQ(0, processed);
- CHECK_EQ(Single::NaN(), StrToF("- +0x5", flags, 0.0f, &processed, &all_used));
+ CHECK_EQ(Single::NaN(), StrToF("- +0x5", flags, 0.0f,
+ &processed, &all_used));
CHECK_EQ(0, processed);
- CHECK_EQ(Single::NaN(), StrToF("+ +0x5", flags, 0.0f, &processed, &all_used));
+ CHECK_EQ(Single::NaN(), StrToF("+ +0x5", flags, 0.0f,
+ &processed, &all_used));
+ CHECK_EQ(0, processed);
+
+ CHECK_EQ(Single::NaN(), StrToF("0x3p0", flags, 0.0f, &processed, &all_used));
+ CHECK_EQ(0, processed);
+
+ CHECK_EQ(Single::NaN(), StrToF("0x.0p0", flags, 0.0f, &processed, &all_used));
+ CHECK_EQ(0, processed);
+
+ CHECK_EQ(Single::NaN(), StrToF("0x3.0p0", flags, 0.0f,
+ &processed, &all_used));
+ CHECK_EQ(0, processed);
+
+ CHECK_EQ(Single::NaN(), StrToF("0x3.p0", flags, 0.0f,
+ &processed, &all_used));
CHECK_EQ(0, processed);
flags = StringToDoubleConverter::ALLOW_HEX;
@@ -3892,6 +3994,20 @@
CHECK_EQ(Single::NaN(), StrToF("+ +0x5", flags, 0.0f, &processed, &all_used));
CHECK_EQ(0, processed);
+ CHECK_EQ(Single::NaN(), StrToF("0x3p0", flags, 0.0f, &processed, &all_used));
+ CHECK_EQ(0, processed);
+
+ CHECK_EQ(Single::NaN(), StrToF("0x.0p0", flags, 0.0f, &processed, &all_used));
+ CHECK_EQ(0, processed);
+
+ CHECK_EQ(Single::NaN(), StrToF("0x3.0p0", flags, 0.0f,
+ &processed, &all_used));
+ CHECK_EQ(0, processed);
+
+ CHECK_EQ(Single::NaN(), StrToF("0x3.p0", flags, 0.0f,
+ &processed, &all_used));
+ CHECK_EQ(0, processed);
+
flags = StringToDoubleConverter::ALLOW_TRAILING_JUNK |
StringToDoubleConverter::ALLOW_HEX;
@@ -4014,6 +4130,19 @@
CHECK_EQ(Single::NaN(), StrToF("+ +0x5", flags, 0.0f, &processed, &all_used));
CHECK_EQ(0, processed);
+ CHECK_EQ(3.0f, StrToF("0x3p0", flags, 0.0f, &processed, &all_used));
+ CHECK_EQ(3, processed);
+
+ CHECK_EQ(Single::NaN(), StrToF("0x.0p0", flags, 0.0f, &processed, &all_used));
+ CHECK_EQ(0, processed);
+
+ CHECK_EQ(3.0f, StrToF("0x3.0p0", flags, 0.0f, &processed, &all_used));
+ CHECK_EQ(3, processed);
+
+ CHECK_EQ(3.0f, StrToF("0x3.p0", flags, 0.0f, &processed, &all_used));
+ CHECK_EQ(3, processed);
+
+
flags = StringToDoubleConverter::ALLOW_TRAILING_JUNK |
StringToDoubleConverter::ALLOW_LEADING_SPACES |
StringToDoubleConverter::ALLOW_TRAILING_SPACES |