mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 17:57:35 +00:00
LibCrypto: Add a way to compare UnsignedBigInteger with double
This patch also make SignedBigInteger::compare_to_double make use of the new function.
This commit is contained in:
parent
e03f014e7a
commit
54b8a2b094
7 changed files with 259 additions and 158 deletions
|
@ -660,9 +660,9 @@ TEST_CASE(test_negative_zero_is_not_allowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE(double_comparisons) {
|
TEST_CASE(double_comparisons) {
|
||||||
#define EXPECT_LESS_THAN(bigint, double_value) EXPECT_EQ(bigint.compare_to_double(double_value), Crypto::SignedBigInteger::CompareResult::DoubleGreaterThanBigInt)
|
#define EXPECT_LESS_THAN(bigint, double_value) EXPECT_EQ(bigint.compare_to_double(double_value), Crypto::UnsignedBigInteger::CompareResult::DoubleGreaterThanBigInt)
|
||||||
#define EXPECT_GREATER_THAN(bigint, double_value) EXPECT_EQ(bigint.compare_to_double(double_value), Crypto::SignedBigInteger::CompareResult::DoubleLessThanBigInt)
|
#define EXPECT_GREATER_THAN(bigint, double_value) EXPECT_EQ(bigint.compare_to_double(double_value), Crypto::UnsignedBigInteger::CompareResult::DoubleLessThanBigInt)
|
||||||
#define EXPECT_EQUAL_TO(bigint, double_value) EXPECT_EQ(bigint.compare_to_double(double_value), Crypto::SignedBigInteger::CompareResult::DoubleEqualsBigInt)
|
#define EXPECT_EQUAL_TO(bigint, double_value) EXPECT_EQ(bigint.compare_to_double(double_value), Crypto::UnsignedBigInteger::CompareResult::DoubleEqualsBigInt)
|
||||||
{ Crypto::SignedBigInteger zero { 0 };
|
{ Crypto::SignedBigInteger zero { 0 };
|
||||||
EXPECT_EQUAL_TO(zero, 0.0);
|
EXPECT_EQUAL_TO(zero, 0.0);
|
||||||
EXPECT_EQUAL_TO(zero, -0.0);
|
EXPECT_EQUAL_TO(zero, -0.0);
|
||||||
|
@ -687,6 +687,14 @@ EXPECT_EQUAL_TO(zero, -0.0);
|
||||||
EXPECT_GREATER_THAN(one, -1.000001);
|
EXPECT_GREATER_THAN(one, -1.000001);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
double double_infinity = HUGE_VAL;
|
||||||
|
VERIFY(isinf(double_infinity));
|
||||||
|
Crypto::SignedBigInteger one { 1 };
|
||||||
|
EXPECT_LESS_THAN(one, double_infinity);
|
||||||
|
EXPECT_GREATER_THAN(one, -double_infinity);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
double double_max_value = NumericLimits<double>::max();
|
double double_max_value = NumericLimits<double>::max();
|
||||||
double double_below_max_value = nextafter(double_max_value, 0.0);
|
double double_below_max_value = nextafter(double_max_value, 0.0);
|
||||||
|
@ -938,18 +946,86 @@ TEST_CASE(bigint_from_double)
|
||||||
#undef SURVIVES_ROUND_TRIP_SIGNED
|
#undef SURVIVES_ROUND_TRIP_SIGNED
|
||||||
#undef SURVIVES_ROUND_TRIP_UNSIGNED
|
#undef SURVIVES_ROUND_TRIP_UNSIGNED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE(unsigned_bigint_double_comparisons)
|
||||||
|
{
|
||||||
|
#define EXPECT_LESS_THAN(bigint, double_value) EXPECT_EQ(bigint.compare_to_double(double_value), Crypto::UnsignedBigInteger::CompareResult::DoubleGreaterThanBigInt)
|
||||||
|
#define EXPECT_GREATER_THAN(bigint, double_value) EXPECT_EQ(bigint.compare_to_double(double_value), Crypto::UnsignedBigInteger::CompareResult::DoubleLessThanBigInt)
|
||||||
|
#define EXPECT_EQUAL_TO(bigint, double_value) EXPECT_EQ(bigint.compare_to_double(double_value), Crypto::UnsignedBigInteger::CompareResult::DoubleEqualsBigInt)
|
||||||
|
|
||||||
|
{
|
||||||
|
Crypto::UnsignedBigInteger zero { 0 };
|
||||||
|
EXPECT_EQUAL_TO(zero, 0.0);
|
||||||
|
EXPECT_EQUAL_TO(zero, -0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Crypto::UnsignedBigInteger one { 1 };
|
||||||
|
EXPECT_EQUAL_TO(one, 1.0);
|
||||||
|
EXPECT_GREATER_THAN(one, -1.0);
|
||||||
|
EXPECT_GREATER_THAN(one, 0.5);
|
||||||
|
EXPECT_GREATER_THAN(one, -0.5);
|
||||||
|
EXPECT_LESS_THAN(one, 1.000001);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
double double_infinity = HUGE_VAL;
|
||||||
|
VERIFY(isinf(double_infinity));
|
||||||
|
Crypto::UnsignedBigInteger one { 1 };
|
||||||
|
EXPECT_LESS_THAN(one, double_infinity);
|
||||||
|
EXPECT_GREATER_THAN(one, -double_infinity);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
double double_max_value = NumericLimits<double>::max();
|
||||||
|
double double_below_max_value = nextafter(double_max_value, 0.0);
|
||||||
|
VERIFY(double_below_max_value < double_max_value);
|
||||||
|
VERIFY(double_below_max_value < (double_max_value - 1.0));
|
||||||
|
auto max_value_in_bigint = Crypto::UnsignedBigInteger::from_base(16, "fffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv);
|
||||||
|
auto max_value_plus_one = max_value_in_bigint.plus(Crypto::UnsignedBigInteger { 1 });
|
||||||
|
auto max_value_minus_one = max_value_in_bigint.minus(Crypto::UnsignedBigInteger { 1 });
|
||||||
|
|
||||||
|
auto below_max_value_in_bigint = Crypto::UnsignedBigInteger::from_base(16, "fffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv);
|
||||||
|
|
||||||
|
EXPECT_EQUAL_TO(max_value_in_bigint, double_max_value);
|
||||||
|
EXPECT_LESS_THAN(max_value_minus_one, double_max_value);
|
||||||
|
EXPECT_GREATER_THAN(max_value_plus_one, double_max_value);
|
||||||
|
EXPECT_LESS_THAN(below_max_value_in_bigint, double_max_value);
|
||||||
|
|
||||||
|
EXPECT_GREATER_THAN(max_value_in_bigint, double_below_max_value);
|
||||||
|
EXPECT_GREATER_THAN(max_value_minus_one, double_below_max_value);
|
||||||
|
EXPECT_GREATER_THAN(max_value_plus_one, double_below_max_value);
|
||||||
|
EXPECT_EQUAL_TO(below_max_value_in_bigint, double_below_max_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
double just_above_255 = bit_cast<double>(0x406fe00000000001ULL);
|
||||||
|
double just_below_255 = bit_cast<double>(0x406fdfffffffffffULL);
|
||||||
|
double double_255 = 255.0;
|
||||||
|
Crypto::UnsignedBigInteger bigint_255 { 255 };
|
||||||
|
|
||||||
|
EXPECT_EQUAL_TO(bigint_255, double_255);
|
||||||
|
EXPECT_GREATER_THAN(bigint_255, just_below_255);
|
||||||
|
EXPECT_LESS_THAN(bigint_255, just_above_255);
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef EXPECT_LESS_THAN
|
||||||
|
#undef EXPECT_GREATER_THAN
|
||||||
|
#undef EXPECT_EQUAL_TO
|
||||||
|
}
|
||||||
|
|
||||||
namespace AK {
|
namespace AK {
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct Formatter<Crypto::SignedBigInteger::CompareResult> : Formatter<StringView> {
|
struct Formatter<Crypto::UnsignedBigInteger::CompareResult> : Formatter<StringView> {
|
||||||
ErrorOr<void> format(FormatBuilder& builder, Crypto::SignedBigInteger::CompareResult const& compare_result)
|
ErrorOr<void> format(FormatBuilder& builder, Crypto::UnsignedBigInteger::CompareResult const& compare_result)
|
||||||
{
|
{
|
||||||
switch (compare_result) {
|
switch (compare_result) {
|
||||||
case Crypto::SignedBigInteger::CompareResult::DoubleEqualsBigInt:
|
case Crypto::UnsignedBigInteger::CompareResult::DoubleEqualsBigInt:
|
||||||
return builder.put_string("Equals"sv);
|
return builder.put_string("Equals"sv);
|
||||||
case Crypto::SignedBigInteger::CompareResult::DoubleLessThanBigInt:
|
case Crypto::UnsignedBigInteger::CompareResult::DoubleLessThanBigInt:
|
||||||
return builder.put_string("LessThan"sv);
|
return builder.put_string("LessThan"sv);
|
||||||
case Crypto::SignedBigInteger::CompareResult::DoubleGreaterThanBigInt:
|
case Crypto::UnsignedBigInteger::CompareResult::DoubleGreaterThanBigInt:
|
||||||
return builder.put_string("GreaterThan"sv);
|
return builder.put_string("GreaterThan"sv);
|
||||||
default:
|
default:
|
||||||
return builder.put_string("???"sv);
|
return builder.put_string("???"sv);
|
||||||
|
|
|
@ -351,153 +351,31 @@ bool SignedBigInteger::operator>=(SignedBigInteger const& other) const
|
||||||
return !(*this < other);
|
return !(*this < other);
|
||||||
}
|
}
|
||||||
|
|
||||||
SignedBigInteger::CompareResult SignedBigInteger::compare_to_double(double value) const
|
UnsignedBigInteger::CompareResult SignedBigInteger::compare_to_double(double value) const
|
||||||
{
|
{
|
||||||
VERIFY(!isnan(value));
|
|
||||||
|
|
||||||
if (isinf(value)) {
|
|
||||||
bool is_positive_infinity = __builtin_isinf_sign(value) > 0;
|
|
||||||
return is_positive_infinity ? CompareResult::DoubleGreaterThanBigInt : CompareResult::DoubleLessThanBigInt;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool bigint_is_negative = m_sign;
|
bool bigint_is_negative = m_sign;
|
||||||
|
|
||||||
bool value_is_negative = value < 0;
|
bool value_is_negative = value < 0;
|
||||||
|
|
||||||
if (value_is_negative != bigint_is_negative)
|
if (value_is_negative != bigint_is_negative)
|
||||||
return bigint_is_negative ? CompareResult::DoubleGreaterThanBigInt : CompareResult::DoubleLessThanBigInt;
|
return bigint_is_negative ? UnsignedBigInteger::CompareResult::DoubleGreaterThanBigInt : UnsignedBigInteger::CompareResult::DoubleLessThanBigInt;
|
||||||
|
|
||||||
// Value is zero, and from above the signs must be the same.
|
// Now both bigint and value have the same sign, so let's compare our magnitudes.
|
||||||
if (value == 0.0) {
|
auto magnitudes_compare_result = m_unsigned_data.compare_to_double(fabs(value));
|
||||||
VERIFY(!value_is_negative && !bigint_is_negative);
|
|
||||||
// Either we are also zero or value is certainly less than us.
|
// If our mangnitudes are euqal, then we're equal.
|
||||||
return is_zero() ? CompareResult::DoubleEqualsBigInt : CompareResult::DoubleLessThanBigInt;
|
if (magnitudes_compare_result == UnsignedBigInteger::CompareResult::DoubleEqualsBigInt)
|
||||||
|
return UnsignedBigInteger::CompareResult::DoubleEqualsBigInt;
|
||||||
|
|
||||||
|
// If we're negative, revert the comparison result, otherwise return the same result.
|
||||||
|
if (value_is_negative) {
|
||||||
|
if (magnitudes_compare_result == UnsignedBigInteger::CompareResult::DoubleLessThanBigInt)
|
||||||
|
return UnsignedBigInteger::CompareResult::DoubleGreaterThanBigInt;
|
||||||
|
else
|
||||||
|
return UnsignedBigInteger::CompareResult::DoubleLessThanBigInt;
|
||||||
|
} else {
|
||||||
|
return magnitudes_compare_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If value is not zero but we are, then since the signs are the same value must be greater.
|
|
||||||
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;
|
|
||||||
|
|
||||||
extractor.d = value;
|
|
||||||
VERIFY(extractor.exponent != (1 << exponent_size) - 1);
|
|
||||||
// Exponent cannot be filled as than we must be NaN or infinity.
|
|
||||||
|
|
||||||
i32 real_exponent = extractor.exponent - exponent_bias;
|
|
||||||
if (real_exponent < 0) {
|
|
||||||
// |value| is less than 1, and we cannot be zero so if we are negative
|
|
||||||
// value must be greater and vice versa.
|
|
||||||
return bigint_is_negative ? CompareResult::DoubleGreaterThanBigInt : CompareResult::DoubleLessThanBigInt;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 bigint_bits_needed = m_unsigned_data.one_based_index_of_highest_set_bit();
|
|
||||||
VERIFY(bigint_bits_needed > 0);
|
|
||||||
|
|
||||||
// Double value is `-1^sign (1.mantissa) * 2^(exponent - bias)` so we need
|
|
||||||
// `exponent - bias + 1` bit to represent doubles value,
|
|
||||||
// for example `exponent - bias` = 3, sign = 0 and mantissa = 0 we get
|
|
||||||
// `-1^0 * 2^3 * 1 = 8` which needs 4 bits to store 8 (0b1000).
|
|
||||||
u32 double_bits_needed = real_exponent + 1;
|
|
||||||
|
|
||||||
if (bigint_bits_needed > double_bits_needed) {
|
|
||||||
// If we need more bits to represent us, we must be of greater magnitude
|
|
||||||
// this means that if we are negative we are below value and if positive above value.
|
|
||||||
return bigint_is_negative ? CompareResult::DoubleGreaterThanBigInt : CompareResult::DoubleLessThanBigInt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bigint_bits_needed < double_bits_needed)
|
|
||||||
return bigint_is_negative ? CompareResult::DoubleLessThanBigInt : CompareResult::DoubleGreaterThanBigInt;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
mantissa_bits |= mantissa_extended_bit;
|
|
||||||
|
|
||||||
// Now we shift value to the left virtually, with `exponent - bias` steps
|
|
||||||
// we then pretend both it and the big int are extended with virtual zeros.
|
|
||||||
using Word = UnsignedBigInteger::Word;
|
|
||||||
auto next_bigint_word = (UnsignedBigInteger::BITS_IN_WORD - 1 + bigint_bits_needed) / UnsignedBigInteger::BITS_IN_WORD;
|
|
||||||
|
|
||||||
VERIFY(next_bigint_word + 1 == trimmed_length());
|
|
||||||
|
|
||||||
auto msb_in_top_word_index = (bigint_bits_needed - 1) % UnsignedBigInteger::BITS_IN_WORD;
|
|
||||||
VERIFY(msb_in_top_word_index == (UnsignedBigInteger::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);
|
|
||||||
|
|
||||||
auto bits_left_in_mantissa = mantissa_size + 1;
|
|
||||||
|
|
||||||
auto get_next_value_bits = [&](size_t num_bits) -> Word {
|
|
||||||
VERIFY(num_bits < 63);
|
|
||||||
VERIFY(bits_left_in_mantissa > 0);
|
|
||||||
if (num_bits > bits_left_in_mantissa)
|
|
||||||
num_bits = bits_left_in_mantissa;
|
|
||||||
|
|
||||||
bits_left_in_mantissa -= num_bits;
|
|
||||||
|
|
||||||
u64 extracted_bits = mantissa_bits & (((1ull << num_bits) - 1) << (64 - num_bits));
|
|
||||||
// Now shift the bits down to put the most significant bit on the num_bits position
|
|
||||||
// this means the rest will be "virtual" zeros.
|
|
||||||
extracted_bits >>= 32;
|
|
||||||
|
|
||||||
// Now shift away the used bits and fit the result into a Word.
|
|
||||||
mantissa_bits <<= num_bits;
|
|
||||||
|
|
||||||
VERIFY(extracted_bits <= NumericLimits<Word>::max());
|
|
||||||
return static_cast<Word>(extracted_bits);
|
|
||||||
};
|
|
||||||
|
|
||||||
auto bits_in_next_bigint_word = msb_in_top_word_index + 1;
|
|
||||||
|
|
||||||
while (next_bigint_word > 0 && bits_left_in_mantissa > 0) {
|
|
||||||
Word bigint_word = words()[next_bigint_word - 1];
|
|
||||||
Word double_word = get_next_value_bits(bits_in_next_bigint_word);
|
|
||||||
|
|
||||||
// For the first bit we have to align it with the top bit of bigint
|
|
||||||
// and for all the other cases bits_in_next_bigint_word is 32 so this does nothing.
|
|
||||||
double_word >>= 32 - bits_in_next_bigint_word;
|
|
||||||
|
|
||||||
if (bigint_word < double_word)
|
|
||||||
return value_is_negative ? CompareResult::DoubleLessThanBigInt : CompareResult::DoubleGreaterThanBigInt;
|
|
||||||
|
|
||||||
if (bigint_word > double_word)
|
|
||||||
return value_is_negative ? CompareResult::DoubleGreaterThanBigInt : CompareResult::DoubleLessThanBigInt;
|
|
||||||
|
|
||||||
--next_bigint_word;
|
|
||||||
bits_in_next_bigint_word = UnsignedBigInteger::BITS_IN_WORD;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are still bits left in bigint than any non zero bit means it has greater magnitude.
|
|
||||||
if (next_bigint_word > 0) {
|
|
||||||
VERIFY(bits_left_in_mantissa == 0);
|
|
||||||
while (next_bigint_word > 0) {
|
|
||||||
if (words()[next_bigint_word - 1] != 0)
|
|
||||||
return value_is_negative ? CompareResult::DoubleGreaterThanBigInt : CompareResult::DoubleLessThanBigInt;
|
|
||||||
--next_bigint_word;
|
|
||||||
}
|
|
||||||
} else if (bits_left_in_mantissa > 0) {
|
|
||||||
VERIFY(next_bigint_word == 0);
|
|
||||||
// Similarly if there are still any bits set in the mantissa it has greater magnitude.
|
|
||||||
if (mantissa_bits != 0)
|
|
||||||
return value_is_negative ? CompareResult::DoubleLessThanBigInt : CompareResult::DoubleGreaterThanBigInt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise if both don't have bits left or the rest of the bits are zero they are equal.
|
|
||||||
return CompareResult::DoubleEqualsBigInt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,13 +140,7 @@ public:
|
||||||
[[nodiscard]] bool operator<(UnsignedBigInteger const& other) const;
|
[[nodiscard]] bool operator<(UnsignedBigInteger const& other) const;
|
||||||
[[nodiscard]] bool operator>(UnsignedBigInteger const& other) const;
|
[[nodiscard]] bool operator>(UnsignedBigInteger const& other) const;
|
||||||
|
|
||||||
enum class CompareResult {
|
[[nodiscard]] UnsignedBigInteger::CompareResult compare_to_double(double) const;
|
||||||
DoubleEqualsBigInt,
|
|
||||||
DoubleLessThanBigInt,
|
|
||||||
DoubleGreaterThanBigInt
|
|
||||||
};
|
|
||||||
|
|
||||||
[[nodiscard]] CompareResult compare_to_double(double) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ensure_sign_is_valid()
|
void ensure_sign_is_valid()
|
||||||
|
|
|
@ -602,6 +602,151 @@ bool UnsignedBigInteger::operator>=(UnsignedBigInteger const& other) const
|
||||||
return *this > other || *this == other;
|
return *this > other || *this == other;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UnsignedBigInteger::CompareResult UnsignedBigInteger::compare_to_double(double value) const
|
||||||
|
{
|
||||||
|
VERIFY(!isnan(value));
|
||||||
|
|
||||||
|
if (isinf(value)) {
|
||||||
|
bool is_positive_infinity = __builtin_isinf_sign(value) > 0;
|
||||||
|
return is_positive_infinity ? CompareResult::DoubleGreaterThanBigInt : CompareResult::DoubleLessThanBigInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool value_is_negative = value < 0;
|
||||||
|
|
||||||
|
if (value_is_negative)
|
||||||
|
return CompareResult::DoubleLessThanBigInt;
|
||||||
|
|
||||||
|
// Value is zero.
|
||||||
|
if (value == 0.0) {
|
||||||
|
VERIFY(!value_is_negative);
|
||||||
|
// Either we are also zero or value is certainly less than us.
|
||||||
|
return is_zero() ? CompareResult::DoubleEqualsBigInt : CompareResult::DoubleLessThanBigInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If value is not zero but we are, value must be greater.
|
||||||
|
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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
i32 real_exponent = extractor.exponent - exponent_bias;
|
||||||
|
if (real_exponent < 0) {
|
||||||
|
// value is less than 1, and we cannot be zero so value must be less.
|
||||||
|
return CompareResult::DoubleLessThanBigInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 bigint_bits_needed = one_based_index_of_highest_set_bit();
|
||||||
|
VERIFY(bigint_bits_needed > 0);
|
||||||
|
|
||||||
|
// Double value is `-1^sign (1.mantissa) * 2^(exponent - bias)` so we need
|
||||||
|
// `exponent - bias + 1` bit to represent doubles value,
|
||||||
|
// for example `exponent - bias` = 3, sign = 0 and mantissa = 0 we get
|
||||||
|
// `-1^0 * 2^3 * 1 = 8` which needs 4 bits to store 8 (0b1000).
|
||||||
|
u32 double_bits_needed = real_exponent + 1;
|
||||||
|
|
||||||
|
// If we need more bits to represent us, we must be of greater value.
|
||||||
|
if (bigint_bits_needed > double_bits_needed)
|
||||||
|
return CompareResult::DoubleLessThanBigInt;
|
||||||
|
// If we need less bits to represent us, we must be of less value.
|
||||||
|
if (bigint_bits_needed < double_bits_needed)
|
||||||
|
return CompareResult::DoubleGreaterThanBigInt;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
mantissa_bits |= mantissa_extended_bit;
|
||||||
|
|
||||||
|
// Now we shift value to the left virtually, with `exponent - bias` steps
|
||||||
|
// we then pretend both it and the big int are extended with virtual zeros.
|
||||||
|
auto next_bigint_word = (BITS_IN_WORD - 1 + bigint_bits_needed) / BITS_IN_WORD;
|
||||||
|
|
||||||
|
VERIFY(next_bigint_word == trimmed_length());
|
||||||
|
|
||||||
|
auto msb_in_top_word_index = (bigint_bits_needed - 1) % BITS_IN_WORD;
|
||||||
|
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);
|
||||||
|
|
||||||
|
auto bits_left_in_mantissa = mantissa_size + 1;
|
||||||
|
|
||||||
|
auto get_next_value_bits = [&](size_t num_bits) -> Word {
|
||||||
|
VERIFY(num_bits < 63);
|
||||||
|
VERIFY(bits_left_in_mantissa > 0);
|
||||||
|
if (num_bits > bits_left_in_mantissa)
|
||||||
|
num_bits = bits_left_in_mantissa;
|
||||||
|
|
||||||
|
bits_left_in_mantissa -= num_bits;
|
||||||
|
|
||||||
|
u64 extracted_bits = mantissa_bits & (((1ull << num_bits) - 1) << (64 - num_bits));
|
||||||
|
// Now shift the bits down to put the most significant bit on the num_bits position
|
||||||
|
// this means the rest will be "virtual" zeros.
|
||||||
|
extracted_bits >>= 32;
|
||||||
|
|
||||||
|
// Now shift away the used bits and fit the result into a Word.
|
||||||
|
mantissa_bits <<= num_bits;
|
||||||
|
|
||||||
|
VERIFY(extracted_bits <= NumericLimits<Word>::max());
|
||||||
|
return static_cast<Word>(extracted_bits);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto bits_in_next_bigint_word = msb_in_top_word_index + 1;
|
||||||
|
|
||||||
|
while (next_bigint_word > 0 && bits_left_in_mantissa > 0) {
|
||||||
|
Word bigint_word = words()[next_bigint_word - 1];
|
||||||
|
Word double_word = get_next_value_bits(bits_in_next_bigint_word);
|
||||||
|
|
||||||
|
// For the first bit we have to align it with the top bit of bigint
|
||||||
|
// and for all the other cases bits_in_next_bigint_word is 32 so this does nothing.
|
||||||
|
double_word >>= 32 - bits_in_next_bigint_word;
|
||||||
|
|
||||||
|
if (bigint_word < double_word)
|
||||||
|
return CompareResult::DoubleGreaterThanBigInt;
|
||||||
|
|
||||||
|
if (bigint_word > double_word)
|
||||||
|
return CompareResult::DoubleLessThanBigInt;
|
||||||
|
|
||||||
|
--next_bigint_word;
|
||||||
|
bits_in_next_bigint_word = BITS_IN_WORD;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are still bits left in bigint than any non zero bit means it has greater value.
|
||||||
|
if (next_bigint_word > 0) {
|
||||||
|
VERIFY(bits_left_in_mantissa == 0);
|
||||||
|
while (next_bigint_word > 0) {
|
||||||
|
if (words()[next_bigint_word - 1] != 0)
|
||||||
|
return CompareResult::DoubleLessThanBigInt;
|
||||||
|
--next_bigint_word;
|
||||||
|
}
|
||||||
|
} else if (bits_left_in_mantissa > 0) {
|
||||||
|
VERIFY(next_bigint_word == 0);
|
||||||
|
// Similarly if there are still any bits set in the mantissa it has greater value.
|
||||||
|
if (mantissa_bits != 0)
|
||||||
|
return CompareResult::DoubleGreaterThanBigInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise if both don't have bits left or the rest of the bits are zero they are equal.
|
||||||
|
return CompareResult::DoubleEqualsBigInt;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> AK::Formatter<Crypto::UnsignedBigInteger>::format(FormatBuilder& fmtbuilder, Crypto::UnsignedBigInteger const& value)
|
ErrorOr<void> AK::Formatter<Crypto::UnsignedBigInteger>::format(FormatBuilder& fmtbuilder, Crypto::UnsignedBigInteger const& value)
|
||||||
|
|
|
@ -121,6 +121,14 @@ public:
|
||||||
[[nodiscard]] bool operator>(UnsignedBigInteger const& other) const;
|
[[nodiscard]] bool operator>(UnsignedBigInteger const& other) const;
|
||||||
[[nodiscard]] bool operator>=(UnsignedBigInteger const& other) const;
|
[[nodiscard]] bool operator>=(UnsignedBigInteger const& other) const;
|
||||||
|
|
||||||
|
enum class CompareResult {
|
||||||
|
DoubleEqualsBigInt,
|
||||||
|
DoubleLessThanBigInt,
|
||||||
|
DoubleGreaterThanBigInt
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] CompareResult compare_to_double(double) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class UnsignedBigIntegerAlgorithms;
|
friend class UnsignedBigIntegerAlgorithms;
|
||||||
// Little endian
|
// Little endian
|
||||||
|
|
|
@ -557,7 +557,7 @@ ThrowCompletionOr<NanosecondsToDaysResult> nanoseconds_to_days(VM& vm, Crypto::S
|
||||||
// 23. If abs(nanoseconds) ≥ abs(dayLengthNs), throw a RangeError exception.
|
// 23. If abs(nanoseconds) ≥ abs(dayLengthNs), throw a RangeError exception.
|
||||||
auto nanoseconds_absolute = nanoseconds.is_negative() ? nanoseconds.negated_value() : nanoseconds;
|
auto nanoseconds_absolute = nanoseconds.is_negative() ? nanoseconds.negated_value() : nanoseconds;
|
||||||
auto compare_result = nanoseconds_absolute.compare_to_double(fabs(day_length_ns.to_double()));
|
auto compare_result = nanoseconds_absolute.compare_to_double(fabs(day_length_ns.to_double()));
|
||||||
if (compare_result == Crypto::SignedBigInteger::CompareResult::DoubleLessThanBigInt || compare_result == Crypto::SignedBigInteger::CompareResult::DoubleEqualsBigInt)
|
if (compare_result == Crypto::UnsignedBigInteger::CompareResult::DoubleLessThanBigInt || compare_result == Crypto::UnsignedBigInteger::CompareResult::DoubleEqualsBigInt)
|
||||||
return vm.throw_completion<RangeError>(ErrorType::TemporalNanosecondsConvertedToRemainderOfNanosecondsLongerThanDayLength);
|
return vm.throw_completion<RangeError>(ErrorType::TemporalNanosecondsConvertedToRemainderOfNanosecondsLongerThanDayLength);
|
||||||
|
|
||||||
// 24. Return the Record { [[Days]]: days, [[Nanoseconds]]: nanoseconds, [[DayLength]]: abs(dayLengthNs) }.
|
// 24. Return the Record { [[Days]]: days, [[Nanoseconds]]: nanoseconds, [[DayLength]]: abs(dayLengthNs) }.
|
||||||
|
|
|
@ -1536,7 +1536,7 @@ ThrowCompletionOr<bool> is_loosely_equal(VM& vm, Value lhs, Value rhs)
|
||||||
auto& number_side = lhs.is_number() ? lhs : rhs;
|
auto& number_side = lhs.is_number() ? lhs : rhs;
|
||||||
auto& bigint_side = lhs.is_number() ? rhs : lhs;
|
auto& bigint_side = lhs.is_number() ? rhs : lhs;
|
||||||
|
|
||||||
return bigint_side.as_bigint().big_integer().compare_to_double(number_side.as_double()) == Crypto::SignedBigInteger::CompareResult::DoubleEqualsBigInt;
|
return bigint_side.as_bigint().big_integer().compare_to_double(number_side.as_double()) == Crypto::UnsignedBigInteger::CompareResult::DoubleEqualsBigInt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 14. Return false.
|
// 14. Return false.
|
||||||
|
@ -1635,10 +1635,10 @@ ThrowCompletionOr<TriState> is_less_than(VM& vm, Value lhs, Value rhs, bool left
|
||||||
VERIFY(!x_numeric.is_nan() && !y_numeric.is_nan());
|
VERIFY(!x_numeric.is_nan() && !y_numeric.is_nan());
|
||||||
if (x_numeric.is_number()) {
|
if (x_numeric.is_number()) {
|
||||||
x_lower_than_y = y_numeric.as_bigint().big_integer().compare_to_double(x_numeric.as_double())
|
x_lower_than_y = y_numeric.as_bigint().big_integer().compare_to_double(x_numeric.as_double())
|
||||||
== Crypto::SignedBigInteger::CompareResult::DoubleLessThanBigInt;
|
== Crypto::UnsignedBigInteger::CompareResult::DoubleLessThanBigInt;
|
||||||
} else {
|
} else {
|
||||||
x_lower_than_y = x_numeric.as_bigint().big_integer().compare_to_double(y_numeric.as_double())
|
x_lower_than_y = x_numeric.as_bigint().big_integer().compare_to_double(y_numeric.as_double())
|
||||||
== Crypto::SignedBigInteger::CompareResult::DoubleGreaterThanBigInt;
|
== Crypto::UnsignedBigInteger::CompareResult::DoubleGreaterThanBigInt;
|
||||||
}
|
}
|
||||||
if (x_lower_than_y)
|
if (x_lower_than_y)
|
||||||
return TriState::True;
|
return TriState::True;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue