mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-21 15:10:19 +00:00
AK: Implement floating-point conversions for big-endian
This commit is contained in:
parent
c99674c6ac
commit
1bc44376c0
Notes:
sideshowbarker
2024-07-17 09:56:35 +09:00
Author: https://github.com/sideeffect42 Commit: https://github.com/LadybirdBrowser/ladybird/commit/1bc44376c0 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/415 Reviewed-by: https://github.com/ADKaster Reviewed-by: https://github.com/DanShaders
3 changed files with 83 additions and 36 deletions
|
@ -25,9 +25,15 @@ union FloatExtractor<f128> {
|
||||||
static constexpr int exponent_bits = 15;
|
static constexpr int exponent_bits = 15;
|
||||||
static constexpr unsigned exponent_max = 32767;
|
static constexpr unsigned exponent_max = 32767;
|
||||||
struct [[gnu::packed]] {
|
struct [[gnu::packed]] {
|
||||||
|
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
ComponentType sign : 1;
|
||||||
|
ComponentType exponent : 15;
|
||||||
|
ComponentType mantissa : 112;
|
||||||
|
# else
|
||||||
ComponentType mantissa : 112;
|
ComponentType mantissa : 112;
|
||||||
ComponentType exponent : 15;
|
ComponentType exponent : 15;
|
||||||
ComponentType sign : 1;
|
ComponentType sign : 1;
|
||||||
|
# endif
|
||||||
};
|
};
|
||||||
f128 d;
|
f128 d;
|
||||||
};
|
};
|
||||||
|
@ -51,9 +57,15 @@ union FloatExtractor<f80> {
|
||||||
// However, since all bit-fiddling float code assumes IEEE floats, it cannot handle this properly.
|
// 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
|
// 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.
|
// 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 mantissa : 64;
|
||||||
ComponentType exponent : 15;
|
ComponentType exponent : 15;
|
||||||
ComponentType sign : 1;
|
ComponentType sign : 1;
|
||||||
|
# endif
|
||||||
};
|
};
|
||||||
f80 d;
|
f80 d;
|
||||||
};
|
};
|
||||||
|
@ -76,9 +88,15 @@ union FloatExtractor<f64> {
|
||||||
// very intuitive and portable behaviour on windows, but it doesn't
|
// very intuitive and portable behaviour on windows, but it doesn't
|
||||||
// work with the msvc ABI.
|
// work with the msvc ABI.
|
||||||
// See <https://github.com/llvm/llvm-project/issues/24757>
|
// 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 mantissa : 52;
|
||||||
ComponentType exponent : 11;
|
ComponentType exponent : 11;
|
||||||
ComponentType sign : 1;
|
ComponentType sign : 1;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
f64 d;
|
f64 d;
|
||||||
};
|
};
|
||||||
|
@ -93,9 +111,15 @@ union FloatExtractor<f32> {
|
||||||
static constexpr int exponent_bits = 8;
|
static constexpr int exponent_bits = 8;
|
||||||
static constexpr ComponentType exponent_max = 255;
|
static constexpr ComponentType exponent_max = 255;
|
||||||
struct [[gnu::packed]] {
|
struct [[gnu::packed]] {
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
ComponentType sign : 1;
|
||||||
|
ComponentType exponent : 8;
|
||||||
|
ComponentType mantissa : 23;
|
||||||
|
#else
|
||||||
ComponentType mantissa : 23;
|
ComponentType mantissa : 23;
|
||||||
ComponentType exponent : 8;
|
ComponentType exponent : 8;
|
||||||
ComponentType sign : 1;
|
ComponentType sign : 1;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
f32 d;
|
f32 d;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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(1e19 <= static_cast<double>(NumericLimits<u64>::max()));
|
||||||
static_assert(1e20 >= 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)
|
constexpr u64 read_eight_digits(char const* string)
|
||||||
{
|
{
|
||||||
u64 val;
|
u64 val;
|
||||||
|
@ -206,40 +204,57 @@ constexpr static u32 eight_digits_to_value(u64 value)
|
||||||
{
|
{
|
||||||
// THIS DOES ABSOLUTELY ASSUME has_eight_digits is true
|
// THIS DOES ABSOLUTELY ASSUME has_eight_digits is true
|
||||||
|
|
||||||
// This trick is based on https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
|
if constexpr (AK::HostIsLittleEndian) {
|
||||||
// FIXME: fast_float uses a slightly different version, but that is far harder
|
// This trick is based on https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
|
||||||
// to understand and does not seem to improve performance substantially.
|
// FIXME: fast_float uses a slightly different version, but that is far harder
|
||||||
// See https://github.com/fastfloat/fast_float/pull/28
|
// 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.)
|
// First convert the digits to their respectively numbers (0x30 -> 0x00 etc.)
|
||||||
value -= 0x3030303030303030;
|
value -= 0x3030303030303030;
|
||||||
|
|
||||||
// Because of little endian the first number will in fact be the least significant
|
// Because of little endian the first number will in fact be the least significant
|
||||||
// bits of value i.e. "12345678" -> 0x0807060504030201
|
// bits of value i.e. "12345678" -> 0x0807060504030201
|
||||||
// This means that we need to shift/multiply each digit with 8 - the byte it is in
|
// 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
|
// 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 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
|
// 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
|
// The values between are "garbage" which we will ignore
|
||||||
value = (value * (256 * 10 + 1)) >> 8;
|
value = (value * (256 * 10 + 1)) >> 8;
|
||||||
// So with our example this gives 0x$$4e$$38$$22$$0c, where $$ is garbage/ignored
|
// So with our example this gives 0x$$4e$$38$$22$$0c, where $$ is garbage/ignored
|
||||||
// In decimal this gives 78 56 34 12
|
// In decimal this gives 78 56 34 12
|
||||||
|
|
||||||
// Now we keep performing the same trick twice more
|
// Now we keep performing the same trick twice more
|
||||||
// First * 100 and shift of 16 (2^16 = 65536) and then shift back
|
// First * 100 and shift of 16 (2^16 = 65536) and then shift back
|
||||||
value = ((value & 0x00FF00FF00FF00FF) * (65536 * 100 + 1)) >> 16;
|
value = ((value & 0x00FF00FF00FF00FF) * (65536 * 100 + 1)) >> 16;
|
||||||
|
|
||||||
// Again with our example this gives 0x$$$$162e$$$$04d2
|
// Again with our example this gives 0x$$$$162e$$$$04d2
|
||||||
// 5678 1234
|
// 5678 1234
|
||||||
|
|
||||||
// And finally with * 10000 and shift of 32 (2^32 = 4294967296)
|
// And finally with * 10000 and shift of 32 (2^32 = 4294967296)
|
||||||
value = ((value & 0x0000FFFF0000FFFF) * (4294967296 * 10000 + 1)) >> 32;
|
value = ((value & 0x0000FFFF0000FFFF) * (4294967296 * 10000 + 1)) >> 32;
|
||||||
|
|
||||||
// With the example this gives 0x$$$$$$$$00bc614e
|
// With the example this gives 0x$$$$$$$$00bc614e
|
||||||
// 12345678
|
// 12345678
|
||||||
// Now we just truncate to the lower part
|
|
||||||
return u32(value);
|
// 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>
|
template<typename IsDoneCallback, typename Has8CharsLeftCallback>
|
||||||
|
|
22
AK/Math.h
22
AK/Math.h
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include <AK/BuiltinWrappers.h>
|
#include <AK/BuiltinWrappers.h>
|
||||||
#include <AK/Concepts.h>
|
#include <AK/Concepts.h>
|
||||||
|
#include <AK/Endian.h>
|
||||||
#include <AK/FloatingPoint.h>
|
#include <AK/FloatingPoint.h>
|
||||||
#include <AK/NumericLimits.h>
|
#include <AK/NumericLimits.h>
|
||||||
#include <AK/StdLibExtraDetails.h>
|
#include <AK/StdLibExtraDetails.h>
|
||||||
|
@ -765,14 +766,21 @@ constexpr T log2(T x)
|
||||||
|
|
||||||
// FIXME: Handle denormalized numbers separately
|
// FIXME: Handle denormalized numbers separately
|
||||||
|
|
||||||
FloatExtractor<T> mantissa_ext {
|
|
||||||
.mantissa = ext.mantissa,
|
|
||||||
.exponent = FloatExtractor<T>::exponent_bias,
|
|
||||||
.sign = ext.sign
|
|
||||||
};
|
|
||||||
|
|
||||||
// (1 <= mantissa < 2)
|
// (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
|
// This is a reconstruction of one of Sun's algorithms
|
||||||
// They use a transformation to lower the problem space,
|
// They use a transformation to lower the problem space,
|
||||||
|
|
Loading…
Reference in a new issue