AK: Implement floating-point conversions for big-endian

This commit is contained in:
Dennis Camera 2024-07-04 11:09:20 +02:00 committed by Andrew Kaster
parent c99674c6ac
commit 1bc44376c0
Notes: sideshowbarker 2024-07-17 09:56:35 +09:00
3 changed files with 83 additions and 36 deletions

View file

@ -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;
};
@ -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;
};
@ -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;
};
@ -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;
};

View file

@ -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;
@ -206,6 +204,7 @@ constexpr static u32 eight_digits_to_value(u64 value)
{
// THIS DOES ABSOLUTELY ASSUME has_eight_digits is true
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.
@ -238,8 +237,24 @@ constexpr static u32 eight_digits_to_value(u64 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>

View file

@ -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>
@ -765,14 +766,21 @@ constexpr T log2(T x)
// FIXME: Handle denormalized numbers separately
FloatExtractor<T> mantissa_ext {
// (1 <= mantissa < 2)
T m;
if constexpr (HostIsLittleEndian) {
m = ((FloatExtractor<T>) {
.mantissa = ext.mantissa,
.exponent = FloatExtractor<T>::exponent_bias,
.sign = ext.sign
};
// (1 <= mantissa < 2)
T m = mantissa_ext.d;
.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,