mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 03:27:45 +00:00
LibCrypto: Add a constructor to (Un)SignedBigInteger taking a double
For now this will assume that the double given is exactly representable as an integer, so no NaN, infinity or rounding.
This commit is contained in:
parent
c87d10365b
commit
528891bf69
5 changed files with 161 additions and 8 deletions
|
@ -864,6 +864,80 @@ TEST_CASE(to_double)
|
||||||
#undef EXPECT_TO_EQUAL_DOUBLE
|
#undef EXPECT_TO_EQUAL_DOUBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE(bigint_from_double)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Crypto::UnsignedBigInteger from_zero { 0.0 };
|
||||||
|
EXPECT(from_zero.is_zero());
|
||||||
|
EXPECT(!from_zero.is_invalid());
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SURVIVES_ROUND_TRIP_UNSIGNED(double_value) \
|
||||||
|
{ \
|
||||||
|
Crypto::UnsignedBigInteger bigint { (double_value) }; \
|
||||||
|
EXPECT_EQ(bigint.to_double(), (double_value)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
SURVIVES_ROUND_TRIP_UNSIGNED(0.0);
|
||||||
|
SURVIVES_ROUND_TRIP_UNSIGNED(1.0);
|
||||||
|
SURVIVES_ROUND_TRIP_UNSIGNED(100000.0);
|
||||||
|
SURVIVES_ROUND_TRIP_UNSIGNED(1000000000000.0);
|
||||||
|
SURVIVES_ROUND_TRIP_UNSIGNED(10000000000000000000.0);
|
||||||
|
SURVIVES_ROUND_TRIP_UNSIGNED(NumericLimits<double>::max());
|
||||||
|
|
||||||
|
SURVIVES_ROUND_TRIP_UNSIGNED(bit_cast<double>(0x4340000000000002ULL));
|
||||||
|
SURVIVES_ROUND_TRIP_UNSIGNED(bit_cast<double>(0x4340000000000001ULL));
|
||||||
|
SURVIVES_ROUND_TRIP_UNSIGNED(bit_cast<double>(0x4340000000000000ULL));
|
||||||
|
|
||||||
|
// Failed on last bits of mantissa
|
||||||
|
SURVIVES_ROUND_TRIP_UNSIGNED(bit_cast<double>(0x7EDFFFFFFFFFFFFFULL));
|
||||||
|
SURVIVES_ROUND_TRIP_UNSIGNED(bit_cast<double>(0x7ed5555555555555ULL));
|
||||||
|
SURVIVES_ROUND_TRIP_UNSIGNED(bit_cast<double>(0x7EDCBA9876543210ULL));
|
||||||
|
|
||||||
|
// Has exactly exponent of 32
|
||||||
|
SURVIVES_ROUND_TRIP_UNSIGNED(bit_cast<double>(0x41f22f74e0000000ULL));
|
||||||
|
|
||||||
|
#define SURVIVES_ROUND_TRIP_SIGNED(double_value) \
|
||||||
|
{ \
|
||||||
|
Crypto::SignedBigInteger bigint_positive { (double_value) }; \
|
||||||
|
EXPECT_EQ(bigint_positive.to_double(), (double_value)); \
|
||||||
|
Crypto::SignedBigInteger bigint_negative { -(double_value) }; \
|
||||||
|
EXPECT_EQ(bigint_negative.to_double(), -(double_value)); \
|
||||||
|
EXPECT(bigint_positive != bigint_negative); \
|
||||||
|
bigint_positive.negate(); \
|
||||||
|
EXPECT(bigint_positive == bigint_negative); \
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Negative zero should be converted to positive zero
|
||||||
|
double const negative_zero = bit_cast<double>(0x8000000000000000);
|
||||||
|
|
||||||
|
// However it should give a bit exact +0.0
|
||||||
|
Crypto::SignedBigInteger from_negative_zero { negative_zero };
|
||||||
|
EXPECT(from_negative_zero.is_zero());
|
||||||
|
EXPECT(!from_negative_zero.is_negative());
|
||||||
|
double result = from_negative_zero.to_double();
|
||||||
|
EXPECT_EQ(result, 0.0);
|
||||||
|
EXPECT_EQ(bit_cast<u64>(result), 0ULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
SURVIVES_ROUND_TRIP_SIGNED(1.0);
|
||||||
|
SURVIVES_ROUND_TRIP_SIGNED(100000.0);
|
||||||
|
SURVIVES_ROUND_TRIP_SIGNED(-1000000000000.0);
|
||||||
|
SURVIVES_ROUND_TRIP_SIGNED(10000000000000000000.0);
|
||||||
|
SURVIVES_ROUND_TRIP_SIGNED(NumericLimits<double>::max());
|
||||||
|
SURVIVES_ROUND_TRIP_SIGNED(NumericLimits<double>::lowest());
|
||||||
|
|
||||||
|
SURVIVES_ROUND_TRIP_SIGNED(bit_cast<double>(0x4340000000000002ULL));
|
||||||
|
SURVIVES_ROUND_TRIP_SIGNED(bit_cast<double>(0x4340000000000001ULL));
|
||||||
|
SURVIVES_ROUND_TRIP_SIGNED(bit_cast<double>(0x4340000000000000ULL));
|
||||||
|
SURVIVES_ROUND_TRIP_SIGNED(bit_cast<double>(0x7EDFFFFFFFFFFFFFULL));
|
||||||
|
SURVIVES_ROUND_TRIP_SIGNED(bit_cast<double>(0x7ed5555555555555ULL));
|
||||||
|
SURVIVES_ROUND_TRIP_SIGNED(bit_cast<double>(0x7EDCBA9876543210ULL));
|
||||||
|
|
||||||
|
#undef SURVIVES_ROUND_TRIP_SIGNED
|
||||||
|
#undef SURVIVES_ROUND_TRIP_UNSIGNED
|
||||||
|
}
|
||||||
namespace AK {
|
namespace AK {
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
|
|
@ -11,6 +11,12 @@
|
||||||
|
|
||||||
namespace Crypto {
|
namespace Crypto {
|
||||||
|
|
||||||
|
SignedBigInteger::SignedBigInteger(double value)
|
||||||
|
: m_sign(value < 0.0)
|
||||||
|
, m_unsigned_data(fabs(value))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
SignedBigInteger SignedBigInteger::import_data(u8 const* ptr, size_t length)
|
SignedBigInteger SignedBigInteger::import_data(u8 const* ptr, size_t length)
|
||||||
{
|
{
|
||||||
bool sign = *ptr;
|
bool sign = *ptr;
|
||||||
|
|
|
@ -43,6 +43,8 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
explicit SignedBigInteger(double value);
|
||||||
|
|
||||||
[[nodiscard]] static SignedBigInteger create_invalid()
|
[[nodiscard]] static SignedBigInteger create_invalid()
|
||||||
{
|
{
|
||||||
return { UnsignedBigInteger::create_invalid(), false };
|
return { UnsignedBigInteger::create_invalid(), false };
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <AK/StringHash.h>
|
#include <AK/StringHash.h>
|
||||||
#include <LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h>
|
#include <LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
namespace Crypto {
|
namespace Crypto {
|
||||||
|
|
||||||
|
@ -33,6 +34,81 @@ 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
|
||||||
|
// also these values don't have a clear BigInteger representation.
|
||||||
|
VERIFY(!isnan(value));
|
||||||
|
VERIFY(!isinf(value));
|
||||||
|
VERIFY(trunc(value) == value);
|
||||||
|
VERIFY(value >= 0.0);
|
||||||
|
|
||||||
|
if (value <= NumericLimits<u32>::max()) {
|
||||||
|
m_words.append(static_cast<u32>(value));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DoubleExtractor extractor;
|
||||||
|
extractor.double_value = value;
|
||||||
|
VERIFY(!extractor.sign);
|
||||||
|
|
||||||
|
i32 real_exponent = extractor.exponent - exponent_bias;
|
||||||
|
VERIFY(real_exponent > 0);
|
||||||
|
|
||||||
|
// Ensure we have enough space, we will need 2^exponent bits, so round up in words
|
||||||
|
auto word_index = (real_exponent + BITS_IN_WORD) / BITS_IN_WORD;
|
||||||
|
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);
|
||||||
|
VERIFY((raw_mantissa & 0xfff0000000000000) == 0x0010000000000000);
|
||||||
|
// Shift it so the bits we need are at the top
|
||||||
|
raw_mantissa <<= 64 - mantissa_size - 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;
|
||||||
|
|
||||||
|
auto top_word_bits_from_mantissa = raw_mantissa >> (64 - top_word_bit_offset);
|
||||||
|
VERIFY(top_word_bits_from_mantissa <= NumericLimits<Word>::max());
|
||||||
|
m_words[word_index - 1] = top_word_bits_from_mantissa;
|
||||||
|
|
||||||
|
--word_index;
|
||||||
|
// Shift used bits away
|
||||||
|
raw_mantissa <<= top_word_bit_offset;
|
||||||
|
i32 bits_in_mantissa = mantissa_size + 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;
|
||||||
|
|
||||||
|
while (word_index > 0 && bits_in_mantissa > 0) {
|
||||||
|
VERIFY((raw_mantissa >> to_word_shift) <= NumericLimits<Word>::max());
|
||||||
|
m_words[word_index - 1] = raw_mantissa >> to_word_shift;
|
||||||
|
raw_mantissa <<= to_word_shift;
|
||||||
|
|
||||||
|
bits_in_mantissa -= BITS_IN_WORD;
|
||||||
|
--word_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
VERIFY(m_words.size() > word_index);
|
||||||
|
VERIFY((m_words.size() - word_index) <= 3);
|
||||||
|
|
||||||
|
// No bits left, otherwise we would have to round
|
||||||
|
VERIFY(raw_mantissa == 0);
|
||||||
|
}
|
||||||
|
|
||||||
UnsignedBigInteger UnsignedBigInteger::create_invalid()
|
UnsignedBigInteger UnsignedBigInteger::create_invalid()
|
||||||
{
|
{
|
||||||
UnsignedBigInteger invalid(0);
|
UnsignedBigInteger invalid(0);
|
||||||
|
@ -265,14 +341,7 @@ double UnsignedBigInteger::to_double(UnsignedBigInteger::RoundingMode rounding_m
|
||||||
VERIFY(rounding_mode == RoundingMode::RoundTowardZero);
|
VERIFY(rounding_mode == RoundingMode::RoundTowardZero);
|
||||||
}
|
}
|
||||||
|
|
||||||
union FloatExtractor {
|
DoubleExtractor extractor;
|
||||||
struct {
|
|
||||||
unsigned long long mantissa : mantissa_size;
|
|
||||||
unsigned exponent : exponent_size;
|
|
||||||
unsigned sign : 1;
|
|
||||||
};
|
|
||||||
double double_value = 0;
|
|
||||||
} extractor;
|
|
||||||
|
|
||||||
extractor.exponent = highest_bit + exponent_bias;
|
extractor.exponent = highest_bit + exponent_bias;
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,8 @@ public:
|
||||||
|
|
||||||
explicit UnsignedBigInteger(u8 const* ptr, size_t length);
|
explicit UnsignedBigInteger(u8 const* ptr, size_t length);
|
||||||
|
|
||||||
|
explicit UnsignedBigInteger(double value);
|
||||||
|
|
||||||
UnsignedBigInteger() = default;
|
UnsignedBigInteger() = default;
|
||||||
|
|
||||||
[[nodiscard]] static UnsignedBigInteger create_invalid();
|
[[nodiscard]] static UnsignedBigInteger create_invalid();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue