Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AK: Implement FloatExtractors and floating-point conversions for big-endian #24688

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions AK/FloatingPoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,15 @@ union FloatExtractor<f128> {
static constexpr int exponent_bits = 15;
static constexpr unsigned exponent_max = 32767;
struct [[gnu::packed]] {
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
ComponentType sign : 1;
ComponentType exponent : 15;
ComponentType mantissa : 112;
# else
ComponentType mantissa : 112;
ComponentType exponent : 15;
ComponentType sign : 1;
# endif
};
f128 d;
};
Expand All @@ -51,9 +57,15 @@ union FloatExtractor<f80> {
// However, since all bit-fiddling float code assumes IEEE floats, it cannot handle this properly.
// If we pretend that 80-bit floats are IEEE floats with 64-bit mantissas, almost everything works correctly
// and we just need a few special cases.
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
ComponentType sign : 1;
ComponentType exponent : 15;
ComponentType mantissa : 64;
# else
ComponentType mantissa : 64;
ComponentType exponent : 15;
ComponentType sign : 1;
# endif
};
f80 d;
};
Expand All @@ -76,9 +88,15 @@ union FloatExtractor<f64> {
// very intuitive and portable behaviour on windows, but it doesn't
// work with the msvc ABI.
// See <https://github.com/llvm/llvm-project/issues/24757>
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
ComponentType sign : 1;
ComponentType exponent : 11;
ComponentType mantissa : 52;
#else
ComponentType mantissa : 52;
ComponentType exponent : 11;
ComponentType sign : 1;
#endif
};
f64 d;
};
Expand All @@ -93,9 +111,15 @@ union FloatExtractor<f32> {
static constexpr int exponent_bits = 8;
static constexpr ComponentType exponent_max = 255;
struct [[gnu::packed]] {
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
ComponentType sign : 1;
ComponentType exponent : 8;
ComponentType mantissa : 23;
#else
ComponentType mantissa : 23;
ComponentType exponent : 8;
ComponentType sign : 1;
#endif
};
f32 d;
};
Expand Down
73 changes: 44 additions & 29 deletions AK/FloatingPointStringConversions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,6 @@ static constexpr auto max_representable_power_of_ten_in_u64 = 19;
static_assert(1e19 <= static_cast<double>(NumericLimits<u64>::max()));
static_assert(1e20 >= static_cast<double>(NumericLimits<u64>::max()));

static_assert(HostIsLittleEndian, "Float parsing currently assumes little endian, this fact is only used in fast parsing of 8 digits at a time"
"\nyou _should_ only need to change read eight_digits to make this big endian compatible.");
constexpr u64 read_eight_digits(char const* string)
{
u64 val;
Expand Down Expand Up @@ -206,40 +204,57 @@ constexpr static u32 eight_digits_to_value(u64 value)
{
// THIS DOES ABSOLUTELY ASSUME has_eight_digits is true

// This trick is based on https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
// FIXME: fast_float uses a slightly different version, but that is far harder
// to understand and does not seem to improve performance substantially.
// See https://github.com/fastfloat/fast_float/pull/28
if constexpr (AK::HostIsLittleEndian) {
// This trick is based on https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
// FIXME: fast_float uses a slightly different version, but that is far harder
// to understand and does not seem to improve performance substantially.
// See https://github.com/fastfloat/fast_float/pull/28

// First convert the digits to their respectively numbers (0x30 -> 0x00 etc.)
value -= 0x3030303030303030;
// First convert the digits to their respectively numbers (0x30 -> 0x00 etc.)
value -= 0x3030303030303030;

// Because of little endian the first number will in fact be the least significant
// bits of value i.e. "12345678" -> 0x0807060504030201
// This means that we need to shift/multiply each digit with 8 - the byte it is in
// So the eight need to go down, and the 01 need to be multiplied with 10000000
// Because of little endian the first number will in fact be the least significant
// bits of value i.e. "12345678" -> 0x0807060504030201
// This means that we need to shift/multiply each digit with 8 - the byte it is in
// So the eight need to go down, and the 01 need to be multiplied with 10000000

// We effectively multiply by 10 and then shift those values to the right (2^8 = 256)
// We then shift the values back down, this leads to 4 digits pairs in the 2 byte parts
// The values between are "garbage" which we will ignore
value = (value * (256 * 10 + 1)) >> 8;
// So with our example this gives 0x$$4e$$38$$22$$0c, where $$ is garbage/ignored
// In decimal this gives 78 56 34 12
// We effectively multiply by 10 and then shift those values to the right (2^8 = 256)
// We then shift the values back down, this leads to 4 digits pairs in the 2 byte parts
// The values between are "garbage" which we will ignore
value = (value * (256 * 10 + 1)) >> 8;
// So with our example this gives 0x$$4e$$38$$22$$0c, where $$ is garbage/ignored
// In decimal this gives 78 56 34 12

// Now we keep performing the same trick twice more
// First * 100 and shift of 16 (2^16 = 65536) and then shift back
value = ((value & 0x00FF00FF00FF00FF) * (65536 * 100 + 1)) >> 16;
// Now we keep performing the same trick twice more
// First * 100 and shift of 16 (2^16 = 65536) and then shift back
value = ((value & 0x00FF00FF00FF00FF) * (65536 * 100 + 1)) >> 16;

// Again with our example this gives 0x$$$$162e$$$$04d2
// 5678 1234
// Again with our example this gives 0x$$$$162e$$$$04d2
// 5678 1234

// And finally with * 10000 and shift of 32 (2^32 = 4294967296)
value = ((value & 0x0000FFFF0000FFFF) * (4294967296 * 10000 + 1)) >> 32;
// And finally with * 10000 and shift of 32 (2^32 = 4294967296)
value = ((value & 0x0000FFFF0000FFFF) * (4294967296 * 10000 + 1)) >> 32;

// With the example this gives 0x$$$$$$$$00bc614e
// 12345678
// Now we just truncate to the lower part
return u32(value);
// With the example this gives 0x$$$$$$$$00bc614e
// 12345678

// Now we just truncate to the lower part
return u32(value);
} else {
value -= 0x3030303030303030;

value = (value & 0x0fL)
+ ((value & (0x0fL << 8)) >> 8) * 10
+ ((value & (0x0fL << 16)) >> 16) * 100
+ ((value & (0x0fL << 24)) >> 24) * 1000
+ ((value & (0x0fL << 32)) >> 32) * 10000
+ ((value & (0x0fL << 40)) >> 40) * 100000
+ ((value & (0x0fL << 48)) >> 48) * 1000000
+ ((value & (0x0fL << 56)) >> 56) * 10000000;

// Now we just truncate to the lower part
return u32(value);
}
}

template<typename IsDoneCallback, typename Has8CharsLeftCallback>
Expand Down
22 changes: 15 additions & 7 deletions AK/Math.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <AK/BuiltinWrappers.h>
#include <AK/Concepts.h>
#include <AK/Endian.h>
#include <AK/FloatingPoint.h>
#include <AK/NumericLimits.h>
#include <AK/StdLibExtraDetails.h>
Expand Down Expand Up @@ -769,14 +770,21 @@ constexpr T log2(T x)

// FIXME: Handle denormalized numbers separately

FloatExtractor<T> mantissa_ext {
.mantissa = ext.mantissa,
.exponent = FloatExtractor<T>::exponent_bias,
.sign = ext.sign
};

// (1 <= mantissa < 2)
T m = mantissa_ext.d;
T m;
if constexpr (HostIsLittleEndian) {
m = ((FloatExtractor<T>) {
.mantissa = ext.mantissa,
.exponent = FloatExtractor<T>::exponent_bias,
.sign = ext.sign })
.d;
} else {
m = ((FloatExtractor<T>) {
.sign = ext.sign,
.exponent = FloatExtractor<T>::exponent_bias,
.mantissa = ext.mantissa })
.d;
}

// This is a reconstruction of one of Sun's algorithms
// They use a transformation to lower the problem space,
Expand Down