1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 20:57:35 +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:
davidot 2022-10-13 02:23:07 +02:00 committed by Linus Groh
parent 53b7f5e6a1
commit 2334cd85a2
3 changed files with 419 additions and 0 deletions

View file

@ -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("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);
}