AK+LibC+LibCrypto: Move FloatExtractor to AK/FloatingPoint.h

This commit is contained in:
Dan Klishch 2022-10-28 12:02:03 -04:00 committed by Andrew Kaster
parent 2d75229192
commit 17c9a3e8d9
Notes: sideshowbarker 2024-07-17 05:21:12 +09:00
3 changed files with 80 additions and 99 deletions

View File

@ -12,6 +12,57 @@
namespace AK {
template<typename T>
union FloatExtractor;
#if ARCH(I386) || ARCH(X86_64) || ARCH(AARCH64)
// This assumes long double is 80 bits, which is true with GCC on Intel platforms
template<>
union FloatExtractor<long double> {
static constexpr int mantissa_bits = 64;
static constexpr unsigned long long mantissa_max = ~0u;
static constexpr int exponent_bias = 16383;
static constexpr int exponent_bits = 15;
static constexpr unsigned exponent_max = 32767;
struct {
unsigned long long mantissa;
unsigned exponent : 15;
unsigned sign : 1;
};
long double d;
};
#endif
template<>
union FloatExtractor<double> {
static constexpr int mantissa_bits = 52;
static constexpr unsigned long long mantissa_max = (1ull << 52) - 1;
static constexpr int exponent_bias = 1023;
static constexpr int exponent_bits = 11;
static constexpr unsigned exponent_max = 2047;
struct {
unsigned long long mantissa : 52;
unsigned exponent : 11;
unsigned sign : 1;
};
double d;
};
template<>
union FloatExtractor<float> {
static constexpr int mantissa_bits = 23;
static constexpr unsigned mantissa_max = (1 << 23) - 1;
static constexpr int exponent_bias = 127;
static constexpr int exponent_bits = 8;
static constexpr unsigned exponent_max = 255;
struct {
unsigned long long mantissa : 23;
unsigned exponent : 8;
unsigned sign : 1;
};
float d;
};
template<size_t S, size_t E, size_t M>
requires(S <= 1 && E >= 1 && M >= 1 && (S + E + M) <= 64) class FloatingPointBits final {
public:
@ -183,6 +234,7 @@ constexpr float convert_to_native_float(I input) { return float_to_float<SingleF
}
using AK::DoubleFloatingPointBits;
using AK::FloatExtractor;
using AK::FloatingPointBits;
using AK::SingleFloatingPointBits;

View File

@ -9,6 +9,7 @@
#include <AK/BuiltinWrappers.h>
#include <AK/ExtraMathConstants.h>
#include <AK/FloatingPoint.h>
#ifndef AK_ARCH_AARCH64
# include <AK/FPControl.h>
#endif
@ -61,57 +62,6 @@ enum class RoundingMode {
ToEven = FE_TONEAREST
};
template<typename T>
union FloatExtractor;
#if ARCH(I386) || ARCH(X86_64) || ARCH(AARCH64)
// This assumes long double is 80 bits, which is true with GCC on Intel platforms
template<>
union FloatExtractor<long double> {
static constexpr int mantissa_bits = 64;
static constexpr unsigned long long mantissa_max = ~0u;
static constexpr int exponent_bias = 16383;
static constexpr int exponent_bits = 15;
static constexpr unsigned exponent_max = 32767;
struct {
unsigned long long mantissa;
unsigned exponent : 15;
unsigned sign : 1;
};
long double d;
};
#endif
template<>
union FloatExtractor<double> {
static constexpr int mantissa_bits = 52;
static constexpr unsigned long long mantissa_max = (1ull << 52) - 1;
static constexpr int exponent_bias = 1023;
static constexpr int exponent_bits = 11;
static constexpr unsigned exponent_max = 2047;
struct {
unsigned long long mantissa : 52;
unsigned exponent : 11;
unsigned sign : 1;
};
double d;
};
template<>
union FloatExtractor<float> {
static constexpr int mantissa_bits = 23;
static constexpr unsigned mantissa_max = (1 << 23) - 1;
static constexpr int exponent_bias = 127;
static constexpr int exponent_bits = 8;
static constexpr unsigned exponent_max = 255;
struct {
unsigned long long mantissa : 23;
unsigned exponent : 8;
unsigned sign : 1;
};
float d;
};
// This is much branchier than it really needs to be
template<typename FloatType>
static FloatType internal_to_integer(FloatType x, RoundingMode rounding_mode)

View File

@ -8,6 +8,7 @@
#include "UnsignedBigInteger.h"
#include <AK/BuiltinWrappers.h>
#include <AK/CharacterTypes.h>
#include <AK/FloatingPoint.h>
#include <AK/StringBuilder.h>
#include <AK/StringHash.h>
#include <LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h>
@ -34,19 +35,6 @@ UnsignedBigInteger::UnsignedBigInteger(u8 const* ptr, size_t length)
}
}
static constexpr u64 mantissa_size = 52;
static constexpr u64 exponent_size = 11;
static constexpr auto exponent_bias = (1 << (exponent_size - 1)) - 1;
union DoubleExtractor {
struct {
unsigned long long mantissa : mantissa_size;
unsigned exponent : exponent_size;
unsigned sign : 1;
};
double double_value = 0;
};
UnsignedBigInteger::UnsignedBigInteger(double value)
{
// Because this is currently only used for LibJS we VERIFY some preconditions
@ -61,11 +49,11 @@ UnsignedBigInteger::UnsignedBigInteger(double value)
return;
}
DoubleExtractor extractor;
extractor.double_value = value;
FloatExtractor<double> extractor;
extractor.d = value;
VERIFY(!extractor.sign);
i32 real_exponent = extractor.exponent - exponent_bias;
i32 real_exponent = extractor.exponent - extractor.exponent_bias;
VERIFY(real_exponent > 0);
// Ensure we have enough space, we will need 2^exponent bits, so round up in words
@ -73,10 +61,10 @@ UnsignedBigInteger::UnsignedBigInteger(double value)
m_words.resize_and_keep_capacity(word_index);
// Now we just need to put the mantissa with explicit 1 bit at the top at the proper location
u64 raw_mantissa = extractor.mantissa | (1ull << mantissa_size);
u64 raw_mantissa = extractor.mantissa | (1ull << extractor.mantissa_bits);
VERIFY((raw_mantissa & 0xfff0000000000000) == 0x0010000000000000);
// Shift it so the bits we need are at the top
raw_mantissa <<= 64 - mantissa_size - 1;
raw_mantissa <<= 64 - extractor.mantissa_bits - 1;
// The initial bit needs to be exactly aligned with exponent, this is 1-indexed
auto top_word_bit_offset = real_exponent % BITS_IN_WORD + 1;
@ -88,7 +76,7 @@ UnsignedBigInteger::UnsignedBigInteger(double value)
--word_index;
// Shift used bits away
raw_mantissa <<= top_word_bit_offset;
i32 bits_in_mantissa = mantissa_size + 1 - top_word_bit_offset;
i32 bits_in_mantissa = extractor.mantissa_bits + 1 - top_word_bit_offset;
// Now just put everything at the top of the next words
constexpr auto to_word_shift = 64 - BITS_IN_WORD;
@ -198,12 +186,14 @@ double UnsignedBigInteger::to_double(UnsignedBigInteger::RoundingMode rounding_m
return 0;
--highest_bit;
using Extractor = FloatExtractor<double>;
// Simple case if less than 2^53 since those number are all exactly representable in doubles
if (highest_bit < mantissa_size + 1)
if (highest_bit < Extractor::mantissa_bits + 1)
return static_cast<double>(to_u64());
// If it uses too many bit to represent in a double return infinity
if (highest_bit > exponent_bias)
if (highest_bit > Extractor::exponent_bias)
return __builtin_huge_val();
// Otherwise we have to take the top 53 bits, use those as the mantissa,
@ -211,11 +201,11 @@ double UnsignedBigInteger::to_double(UnsignedBigInteger::RoundingMode rounding_m
// so we have to ignore the very top bit.
// Since we extract at most 53 bits it will take at most 3 words
static_assert(BITS_IN_WORD * 3 >= (mantissa_size + 1));
static_assert(BITS_IN_WORD * 3 >= (Extractor::mantissa_bits + 1));
constexpr auto bits_in_u64 = 64;
static_assert(bits_in_u64 > mantissa_size + 1);
static_assert(bits_in_u64 > Extractor::mantissa_bits + 1);
auto bits_to_read = min(mantissa_size, highest_bit);
auto bits_to_read = min(static_cast<size_t>(Extractor::mantissa_bits), highest_bit);
auto last_word_index = trimmed_length();
VERIFY(last_word_index > 0);
@ -267,7 +257,7 @@ double UnsignedBigInteger::to_double(UnsignedBigInteger::RoundingMode rounding_m
}
// Now the mantissa should be complete so shift it down
mantissa >>= bits_in_u64 - mantissa_size;
mantissa >>= bits_in_u64 - Extractor::mantissa_bits;
if (rounding_mode == RoundingMode::IEEERoundAndTiesToEvenMantissa) {
bool round_up = false;
@ -327,13 +317,13 @@ double UnsignedBigInteger::to_double(UnsignedBigInteger::RoundingMode rounding_m
if (round_up) {
++mantissa;
if ((mantissa & (1ull << mantissa_size)) != 0) {
if ((mantissa & (1ull << Extractor::mantissa_bits)) != 0) {
// we overflowed the mantissa
mantissa = 0;
highest_bit++;
// In which case it is possible we have to round to infinity
if (highest_bit > exponent_bias)
if (highest_bit > Extractor::exponent_bias)
return __builtin_huge_val();
}
}
@ -341,14 +331,13 @@ double UnsignedBigInteger::to_double(UnsignedBigInteger::RoundingMode rounding_m
VERIFY(rounding_mode == RoundingMode::RoundTowardZero);
}
DoubleExtractor extractor;
extractor.exponent = highest_bit + exponent_bias;
Extractor extractor;
extractor.exponent = highest_bit + extractor.exponent_bias;
VERIFY((mantissa & 0xfff0000000000000) == 0);
extractor.mantissa = mantissa;
return extractor.double_value;
return extractor.d;
}
void UnsignedBigInteger::set_to_0()
@ -627,25 +616,15 @@ UnsignedBigInteger::CompareResult UnsignedBigInteger::compare_to_double(double v
if (is_zero())
return CompareResult::DoubleGreaterThanBigInt;
constexpr u64 mantissa_size = 52;
constexpr u64 exponent_size = 11;
constexpr auto exponent_bias = (1 << (exponent_size - 1)) - 1;
union FloatExtractor {
struct {
unsigned long long mantissa : mantissa_size;
unsigned exponent : exponent_size;
unsigned sign : 1;
};
double d;
} extractor;
FloatExtractor<double> extractor;
extractor.d = value;
// Value cannot be negative at this point.
VERIFY(extractor.sign == 0);
// Exponent cannot be all set, as then we must be NaN or infinity.
VERIFY(extractor.exponent != (1 << exponent_size) - 1);
VERIFY(extractor.exponent != (1 << extractor.exponent_bits) - 1);
i32 real_exponent = extractor.exponent - exponent_bias;
i32 real_exponent = extractor.exponent - extractor.exponent_bias;
if (real_exponent < 0) {
// value is less than 1, and we cannot be zero so value must be less.
return CompareResult::DoubleLessThanBigInt;
@ -670,7 +649,7 @@ UnsignedBigInteger::CompareResult UnsignedBigInteger::compare_to_double(double v
u64 mantissa_bits = extractor.mantissa;
// We add the bit which represents the 1. of the double value calculation.
constexpr u64 mantissa_extended_bit = 1ull << mantissa_size;
constexpr u64 mantissa_extended_bit = 1ull << extractor.mantissa_bits;
mantissa_bits |= mantissa_extended_bit;
@ -684,9 +663,9 @@ UnsignedBigInteger::CompareResult UnsignedBigInteger::compare_to_double(double v
VERIFY(msb_in_top_word_index == (BITS_IN_WORD - count_leading_zeroes(words()[next_bigint_word - 1]) - 1));
// We will keep the bits which are still valid in the mantissa at the top of mantissa bits.
mantissa_bits <<= 64 - (mantissa_size + 1);
mantissa_bits <<= 64 - (extractor.mantissa_bits + 1);
auto bits_left_in_mantissa = mantissa_size + 1;
auto bits_left_in_mantissa = static_cast<size_t>(extractor.mantissa_bits) + 1;
auto get_next_value_bits = [&](size_t num_bits) -> Word {
VERIFY(num_bits < 63);