mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 07:38:10 +00:00
AK: Add an exact and fast hex float parsing algorithm
Similar to decimal floating point parsing the current strtod hex float parsing gives a lot of incorrect results. We can use a similar technique as with decimal parsing however hex floats are much simpler as we don't need to scale with a power of 5. For hex floats we just provide the parse_first_hexfloat API as there is currently no need for a parse_hexfloat_completely API. Again the accepted input for parse_first_hexfloat is very lenient and any validation should be done before calling this method.
This commit is contained in:
parent
53b7f5e6a1
commit
2334cd85a2
3 changed files with 419 additions and 0 deletions
|
@ -2015,4 +2015,239 @@ template Optional<double> parse_floating_point_completely(char const* start, cha
|
||||||
|
|
||||||
template Optional<float> parse_floating_point_completely(char const* start, char const* end);
|
template Optional<float> parse_floating_point_completely(char const* start, char const* end);
|
||||||
|
|
||||||
|
struct HexFloatParseResult {
|
||||||
|
bool is_negative = false;
|
||||||
|
bool valid = false;
|
||||||
|
char const* last_parsed = nullptr;
|
||||||
|
u64 mantissa = 0;
|
||||||
|
i64 exponent = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static HexFloatParseResult parse_hexfloat(char const* start)
|
||||||
|
{
|
||||||
|
HexFloatParseResult result {};
|
||||||
|
if (start == nullptr || *start == '\0')
|
||||||
|
return result;
|
||||||
|
|
||||||
|
char const* parse_head = start;
|
||||||
|
bool any_digits = false;
|
||||||
|
bool truncated_non_zero = false;
|
||||||
|
|
||||||
|
if (*parse_head == '-') {
|
||||||
|
result.is_negative = true;
|
||||||
|
++parse_head;
|
||||||
|
|
||||||
|
if (*parse_head == '\0' || (!is_ascii_hex_digit(*parse_head) && *parse_head != floating_point_decimal_separator))
|
||||||
|
return result;
|
||||||
|
} else if (*parse_head == '+') {
|
||||||
|
++parse_head;
|
||||||
|
|
||||||
|
if (*parse_head == '\0' || (!is_ascii_hex_digit(*parse_head) && *parse_head != floating_point_decimal_separator))
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (*parse_head == '0' && (*(parse_head + 1) != '\0') && (*(parse_head + 1) == 'x' || *(parse_head + 1) == 'X')) {
|
||||||
|
// Skip potential 0[xX], we have to do this here since the sign comes at the front
|
||||||
|
parse_head += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto add_mantissa_digit = [&] {
|
||||||
|
any_digits = true;
|
||||||
|
|
||||||
|
// We assume you already checked this is actually a digit
|
||||||
|
auto digit = parse_ascii_hex_digit(*parse_head);
|
||||||
|
|
||||||
|
// Because the power of sixteen is just scaling of power of two we don't
|
||||||
|
// need to keep all the remaining digits beyond the first 52 bits, just because
|
||||||
|
// it's easy we store the first 16 digits. However for rounding we do need to parse
|
||||||
|
// all the digits and keep track if we see any non zero one.
|
||||||
|
if (result.mantissa < (1ull << 60)) {
|
||||||
|
result.mantissa = (result.mantissa * 16) + digit;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (digit != 0)
|
||||||
|
truncated_non_zero = true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
while (*parse_head != '\0' && is_ascii_hex_digit(*parse_head)) {
|
||||||
|
add_mantissa_digit();
|
||||||
|
|
||||||
|
++parse_head;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*parse_head != '\0' && *parse_head == floating_point_decimal_separator) {
|
||||||
|
++parse_head;
|
||||||
|
i64 digits_after_separator = 0;
|
||||||
|
while (*parse_head != '\0' && is_ascii_hex_digit(*parse_head)) {
|
||||||
|
// Track how many characters we actually read into the mantissa
|
||||||
|
digits_after_separator += add_mantissa_digit() ? 1 : 0;
|
||||||
|
|
||||||
|
++parse_head;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We parsed x digits after the dot so need to multiply with 2^(-x * 4)
|
||||||
|
// Since every digit is 4 bits
|
||||||
|
result.exponent = -digits_after_separator * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!any_digits)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (*parse_head != '\0' && (*parse_head == 'p' || *parse_head == 'P')) {
|
||||||
|
[&] {
|
||||||
|
auto const* head_before_p = parse_head;
|
||||||
|
ArmedScopeGuard reset_ptr { [&] { parse_head = head_before_p; } };
|
||||||
|
++parse_head;
|
||||||
|
|
||||||
|
if (*parse_head == '\0')
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool exponent_is_negative = false;
|
||||||
|
i64 explicit_exponent = 0;
|
||||||
|
|
||||||
|
if (*parse_head == '-' || *parse_head == '+') {
|
||||||
|
exponent_is_negative = *parse_head == '-';
|
||||||
|
++parse_head;
|
||||||
|
if (*parse_head == '\0')
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_ascii_digit(*parse_head))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// We have at least one digit (with optional preceding sign) so we will not reset
|
||||||
|
reset_ptr.disarm();
|
||||||
|
|
||||||
|
while (*parse_head != '\0' && is_ascii_digit(*parse_head)) {
|
||||||
|
// If we hit exponent overflow the number is so huge we are in trouble anyway, see
|
||||||
|
// a comment in parse_numbers.
|
||||||
|
if (explicit_exponent < 0x10000000)
|
||||||
|
explicit_exponent = 10 * explicit_exponent + (*parse_head - '0');
|
||||||
|
++parse_head;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exponent_is_negative)
|
||||||
|
explicit_exponent = -explicit_exponent;
|
||||||
|
|
||||||
|
result.exponent += explicit_exponent;
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.valid = true;
|
||||||
|
|
||||||
|
// Round up exactly halfway with truncated non zeros, but don't if it would cascade up
|
||||||
|
if (truncated_non_zero && (result.mantissa & 0xF) != 0xF) {
|
||||||
|
VERIFY(result.mantissa >= 0x1000'0000'0000'0000);
|
||||||
|
result.mantissa |= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.last_parsed = parse_head;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<FloatingPoint T>
|
||||||
|
static FloatingPointBuilder build_hex_float(HexFloatParseResult& parse_result)
|
||||||
|
{
|
||||||
|
using FloatingPointRepr = FloatingPointInfo<T>;
|
||||||
|
VERIFY(parse_result.mantissa != 0);
|
||||||
|
|
||||||
|
if (parse_result.exponent >= FloatingPointRepr::infinity_exponent())
|
||||||
|
return FloatingPointBuilder::infinity<T>();
|
||||||
|
|
||||||
|
auto leading_zeros = count_leading_zeroes(parse_result.mantissa);
|
||||||
|
u64 normalized_mantissa = parse_result.mantissa << leading_zeros;
|
||||||
|
|
||||||
|
// No need to multiply with some power of 5 here the exponent is already a power of 2.
|
||||||
|
|
||||||
|
u8 upperbit = normalized_mantissa >> 63;
|
||||||
|
FloatingPointBuilder parts;
|
||||||
|
parts.mantissa = normalized_mantissa >> (upperbit + 64 - FloatingPointRepr::mantissa_bits() - 3);
|
||||||
|
|
||||||
|
parts.exponent = parse_result.exponent + upperbit - leading_zeros + FloatingPointRepr::exponent_bias() + 62;
|
||||||
|
|
||||||
|
if (parts.exponent <= 0) {
|
||||||
|
// subnormal
|
||||||
|
if (-parts.exponent + 1 >= 64) {
|
||||||
|
parts.mantissa = 0;
|
||||||
|
parts.exponent = 0;
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.mantissa >>= -parts.exponent + 1;
|
||||||
|
parts.mantissa += parts.mantissa & 1;
|
||||||
|
parts.mantissa >>= 1;
|
||||||
|
|
||||||
|
if (parts.mantissa < (1ull << FloatingPointRepr::mantissa_bits())) {
|
||||||
|
parts.exponent = 0;
|
||||||
|
} else {
|
||||||
|
parts.exponent = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here we don't have to only do this halfway check for some exponents
|
||||||
|
if ((parts.mantissa & 0b11) == 0b01) {
|
||||||
|
// effectively all discard bits from z.high are 0
|
||||||
|
if (normalized_mantissa == (parts.mantissa << (upperbit + 64 - FloatingPointRepr::mantissa_bits() - 3)))
|
||||||
|
parts.mantissa &= ~u64(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.mantissa += parts.mantissa & 1;
|
||||||
|
parts.mantissa >>= 1;
|
||||||
|
|
||||||
|
if (parts.mantissa >= (2ull << FloatingPointRepr::mantissa_bits())) {
|
||||||
|
parts.mantissa = 1ull << FloatingPointRepr::mantissa_bits();
|
||||||
|
++parts.exponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.mantissa &= ~(1ull << FloatingPointRepr::mantissa_bits());
|
||||||
|
|
||||||
|
if (parts.exponent >= FloatingPointRepr::infinity_exponent()) {
|
||||||
|
parts.mantissa = 0;
|
||||||
|
parts.exponent = FloatingPointRepr::infinity_exponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<FloatingPoint T>
|
||||||
|
FloatingPointParseResults<T> parse_first_hexfloat_until_zero_character(char const* start)
|
||||||
|
{
|
||||||
|
using FloatingPointRepr = FloatingPointInfo<T>;
|
||||||
|
auto parse_result = parse_hexfloat(start);
|
||||||
|
|
||||||
|
if (!parse_result.valid)
|
||||||
|
return { nullptr, FloatingPointError::NoOrInvalidInput, __builtin_nan("") };
|
||||||
|
|
||||||
|
FloatingPointParseResults<T> full_result {};
|
||||||
|
full_result.end_ptr = parse_result.last_parsed;
|
||||||
|
|
||||||
|
// We special case this to be able to differentiate between 0 and values rounded down to 0
|
||||||
|
|
||||||
|
if (parse_result.mantissa == 0) {
|
||||||
|
full_result.value = 0.;
|
||||||
|
return full_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = build_hex_float<T>(parse_result);
|
||||||
|
full_result.value = result.template to_value<T>(parse_result.is_negative);
|
||||||
|
|
||||||
|
if (result.exponent == FloatingPointRepr::infinity_exponent()) {
|
||||||
|
VERIFY(result.mantissa == 0);
|
||||||
|
full_result.error = FloatingPointError::OutOfRange;
|
||||||
|
} else if (result.mantissa == 0 && result.exponent == 0) {
|
||||||
|
full_result.error = FloatingPointError::RoundedDownToZero;
|
||||||
|
}
|
||||||
|
|
||||||
|
return full_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template FloatingPointParseResults<double> parse_first_hexfloat_until_zero_character(char const* start);
|
||||||
|
|
||||||
|
template FloatingPointParseResults<float> parse_first_hexfloat_until_zero_character(char const* start);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,24 @@ FloatingPointParseResults<T> parse_first_floating_point_until_zero_character(cha
|
||||||
template<FloatingPoint T = double>
|
template<FloatingPoint T = double>
|
||||||
Optional<T> parse_floating_point_completely(char const* start, char const* end);
|
Optional<T> parse_floating_point_completely(char const* start, char const* end);
|
||||||
|
|
||||||
|
/// This function finds the first floating point as a hex float within [start, end).
|
||||||
|
/// The accepted format is intentionally as lenient as possible. If your format is
|
||||||
|
/// stricter you must validate it first. The format accepts:
|
||||||
|
/// - An optional sign, both + and - are supported
|
||||||
|
/// - Optionally either 0x or OX
|
||||||
|
/// - 0 or more hexadecimal digits, with leading zeros allowed [1]
|
||||||
|
/// - A decimal point '.', which can have no digits after it
|
||||||
|
/// - 0 or more hexadecimal digits, unless the first digits [1] doesn't have any digits,
|
||||||
|
/// then this must have at least one
|
||||||
|
/// - An exponent 'p' or 'P' followed by an optional sign '+' or '-' and at least one decimal digit
|
||||||
|
/// NOTE: The exponent is _not_ hexadecimal and gives powers of 2 not 16.
|
||||||
|
/// This function additionally detects out of range values which have been rounded to
|
||||||
|
/// [-]infinity or 0 and gives the next character to read after the floating point.
|
||||||
|
template<FloatingPoint T = double>
|
||||||
|
FloatingPointParseResults<T> parse_first_hexfloat_until_zero_character(char const* start);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using AK::parse_first_floating_point;
|
using AK::parse_first_floating_point;
|
||||||
|
using AK::parse_first_hexfloat_until_zero_character;
|
||||||
using AK::parse_floating_point_completely;
|
using AK::parse_floating_point_completely;
|
||||||
|
|
|
@ -410,3 +410,170 @@ TEST_CASE(parse_completely_must_be_just_floating_point)
|
||||||
EXPECT_PARSE_COMPLETELY_TO_FAIL("1=234567890");
|
EXPECT_PARSE_COMPLETELY_TO_FAIL("1=234567890");
|
||||||
EXPECT_PARSE_COMPLETELY_TO_FAIL("1234567=890");
|
EXPECT_PARSE_COMPLETELY_TO_FAIL("1234567=890");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static double newhex(char const* view)
|
||||||
|
{
|
||||||
|
auto value = parse_first_hexfloat_until_zero_character<double>(view);
|
||||||
|
VERIFY(value.error == AK::FloatingPointError::None);
|
||||||
|
return value.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float newhexf(char const* view)
|
||||||
|
{
|
||||||
|
auto value = parse_first_hexfloat_until_zero_character<float>(view);
|
||||||
|
VERIFY(value.error == AK::FloatingPointError::None);
|
||||||
|
return value.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(hexfloat)
|
||||||
|
{
|
||||||
|
|
||||||
|
#define DOES_PARSE_HEX_DOUBLE_LIKE_CPP(value) \
|
||||||
|
do { \
|
||||||
|
EXPECT_EQ(static_cast<double>(value), newhex(#value)); \
|
||||||
|
EXPECT_EQ(-static_cast<double>(value), newhex("-" #value)); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#define DOES_PARSE_HEX_FLOAT_LIKE_CPP(value) \
|
||||||
|
do { \
|
||||||
|
EXPECT_EQ(static_cast<float>(value##f), newhexf(#value)); \
|
||||||
|
EXPECT_EQ(-static_cast<float>(value##f), newhexf("-" #value)); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#define DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(value) \
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(value); \
|
||||||
|
DOES_PARSE_HEX_FLOAT_LIKE_CPP(value)
|
||||||
|
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x123456789ABCDEFp0);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x123456789ABCDEFp+0);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x123456789ABCDEFp-0);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x123456789ABCDEF.p-0);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x123456789ABCDEF.123456789ABCDEFp-0);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x123456789ABCDEF.123456789ABCDEFp-1);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x123456789ABCDEF.123456789ABCDEFp+1);
|
||||||
|
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp+52);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c0p+52);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c00p+52);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c000p+52);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c001p+52);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p+52);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c10001p+52);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c8p+52);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c8001p+52);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c80000000000000000000000000000000000000000000000000000000001p+52);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c80000000000000000000000000000000000000000000000000000000000p+52);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffp+52);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c9p+52);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c9001p+52);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x180eafb89ba47c9.001p+52);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x180eafb89ba47c9.001p-4);
|
||||||
|
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp-1075);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p-1075);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp-1040);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p-1040);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp-999);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p-999);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp-788);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p-788);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp-632);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p-632);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp-408);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p-408);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp-189);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p-189);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp-76);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p-76);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp-52);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p-52);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp-25);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p-25);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp-13);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p-13);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp-3);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p-3);
|
||||||
|
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp+3);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p+3);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp+6);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p+6);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp+13);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p+13);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp+19);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p+19);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp+154);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p+154);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp+298);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p+298);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp+455);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p+455);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp+692);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p+692);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp+901);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p+901);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47cp+1023);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.80eafb89ba47c1p+1023);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x.80eafb89ba47cp+1024);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x.80eafb89ba47c1p+1024);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x.080eafb89ba47cp+1025);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x.080eafb89ba47c1p+1025);
|
||||||
|
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.c5e1463479f8ep+218);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.c5e1463479f8e8p+218);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.c5e1463479f8e80p+218);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.c5e1463479f8e800p+218);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.c5e1463479f8e8001p+218);
|
||||||
|
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.42100a53adbd5p-1024);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.d542100a53adbp-1023);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.fffffffffffffp-1023);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.fffffffffffff9p-1023);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.fffffffffffff8p-1023);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.fffffffffffff7p-1023);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.fffffffffffff800000001p-1023);
|
||||||
|
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1p-1022);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x2p-1022);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x3p-1022);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x1.0p-1022);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x000000000000000000000000000000000001.0p-1022);
|
||||||
|
DOES_PARSE_HEX_DOUBLE_LIKE_CPP(0x000000000000000000000000000000000001.000000000000000000p-1022);
|
||||||
|
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0xCap0);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0xCAp0);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0xcAp0);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0xcAP0);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0xcaP0);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0xcap0);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0xcap1);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0xca.p1);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0xc.ap1);
|
||||||
|
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x1.p0);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x11.p0);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x11.p1);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x11.p2);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x11.p-2);
|
||||||
|
DOES_PARSE_HEX_FLOAT_AND_DOUBLE_LIKE_CPP(0x11.p-0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(invalid_hex_floats)
|
||||||
|
{
|
||||||
|
#define EXPECT_HEX_PARSE_TO_VALUE_AND_CONSUME_CHARS(string_value, double_value, chars_parsed) \
|
||||||
|
do { \
|
||||||
|
char const* c_str = string_value; \
|
||||||
|
auto result = parse_first_hexfloat_until_zero_character<double>(c_str); \
|
||||||
|
EXPECT(result.error == AK::FloatingPointError::None); \
|
||||||
|
EXPECT_EQ(bit_cast<u64>(result.value), bit_cast<u64>(static_cast<double>(double_value))); \
|
||||||
|
EXPECT_EQ(result.end_ptr - c_str, chars_parsed); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
EXPECT_HEX_PARSE_TO_VALUE_AND_CONSUME_CHARS("0xab.cdpef", 0xab.cdp0, 7);
|
||||||
|
EXPECT_HEX_PARSE_TO_VALUE_AND_CONSUME_CHARS("0xab.cdPef", 0xab.cdp0, 7);
|
||||||
|
EXPECT_HEX_PARSE_TO_VALUE_AND_CONSUME_CHARS("0xab.cdPEf", 0xab.cdp0, 7);
|
||||||
|
EXPECT_HEX_PARSE_TO_VALUE_AND_CONSUME_CHARS("0xab.cdPEF", 0xab.cdp0, 7);
|
||||||
|
EXPECT_HEX_PARSE_TO_VALUE_AND_CONSUME_CHARS("0xAB.cdPEF", 0xab.cdp0, 7);
|
||||||
|
EXPECT_HEX_PARSE_TO_VALUE_AND_CONSUME_CHARS("0xABCDPEF", 0xabcdp0, 6);
|
||||||
|
EXPECT_HEX_PARSE_TO_VALUE_AND_CONSUME_CHARS("0xCAPE", 0xCAp0, 4);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue