diff --git a/Tests/LibCrypto/TestBigInteger.cpp b/Tests/LibCrypto/TestBigInteger.cpp index 308ef61fc9..117f5e1891 100644 --- a/Tests/LibCrypto/TestBigInteger.cpp +++ b/Tests/LibCrypto/TestBigInteger.cpp @@ -659,96 +659,177 @@ TEST_CASE(test_negative_zero_is_not_allowed) EXPECT(!zero.is_negative()); } -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_GREATER_THAN(bigint, double_value) EXPECT_EQ(bigint.compare_to_double(double_value), Crypto::SignedBigInteger::CompareResult::DoubleLessThanBigInt) #define EXPECT_EQUAL_TO(bigint, double_value) EXPECT_EQ(bigint.compare_to_double(double_value), Crypto::SignedBigInteger::CompareResult::DoubleEqualsBigInt) - { - Crypto::SignedBigInteger zero { 0 }; - EXPECT_EQUAL_TO(zero, 0.0); - EXPECT_EQUAL_TO(zero, -0.0); - } + { Crypto::SignedBigInteger zero { 0 }; +EXPECT_EQUAL_TO(zero, 0.0); +EXPECT_EQUAL_TO(zero, -0.0); +} - { - Crypto::SignedBigInteger 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); +{ + Crypto::SignedBigInteger 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); - one.negate(); - auto const& negative_one = one; - EXPECT_EQUAL_TO(negative_one, -1.0); - EXPECT_LESS_THAN(negative_one, 1.0); - EXPECT_LESS_THAN(one, 0.5); - EXPECT_LESS_THAN(one, -0.5); - EXPECT_GREATER_THAN(one, -1.5); - EXPECT_LESS_THAN(one, 1.000001); - EXPECT_GREATER_THAN(one, -1.000001); - } + one.negate(); + auto const& negative_one = one; + EXPECT_EQUAL_TO(negative_one, -1.0); + EXPECT_LESS_THAN(negative_one, 1.0); + EXPECT_LESS_THAN(one, 0.5); + EXPECT_LESS_THAN(one, -0.5); + EXPECT_GREATER_THAN(one, -1.5); + EXPECT_LESS_THAN(one, 1.000001); + EXPECT_GREATER_THAN(one, -1.000001); +} - { - double double_max_value = NumericLimits::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::SignedBigInteger::from_base(16, "fffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv); - auto max_value_plus_one = max_value_in_bigint.plus(Crypto::SignedBigInteger { 1 }); - auto max_value_minus_one = max_value_in_bigint.minus(Crypto::SignedBigInteger { 1 }); +{ + double double_max_value = NumericLimits::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::SignedBigInteger::from_base(16, "fffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv); + auto max_value_plus_one = max_value_in_bigint.plus(Crypto::SignedBigInteger { 1 }); + auto max_value_minus_one = max_value_in_bigint.minus(Crypto::SignedBigInteger { 1 }); - auto below_max_value_in_bigint = Crypto::SignedBigInteger::from_base(16, "fffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv); + auto below_max_value_in_bigint = Crypto::SignedBigInteger::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_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); - } + 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 double_min_value = NumericLimits::lowest(); - double double_above_min_value = nextafter(double_min_value, 0.0); - VERIFY(double_above_min_value > double_min_value); - VERIFY(double_above_min_value > (double_min_value + 1.0)); - auto min_value_in_bigint = Crypto::SignedBigInteger::from_base(16, "-fffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv); - auto min_value_plus_one = min_value_in_bigint.plus(Crypto::SignedBigInteger { 1 }); - auto min_value_minus_one = min_value_in_bigint.minus(Crypto::SignedBigInteger { 1 }); +{ + double double_min_value = NumericLimits::lowest(); + double double_above_min_value = nextafter(double_min_value, 0.0); + VERIFY(double_above_min_value > double_min_value); + VERIFY(double_above_min_value > (double_min_value + 1.0)); + auto min_value_in_bigint = Crypto::SignedBigInteger::from_base(16, "-fffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv); + auto min_value_plus_one = min_value_in_bigint.plus(Crypto::SignedBigInteger { 1 }); + auto min_value_minus_one = min_value_in_bigint.minus(Crypto::SignedBigInteger { 1 }); - auto above_min_value_in_bigint = Crypto::SignedBigInteger::from_base(16, "-fffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv); + auto above_min_value_in_bigint = Crypto::SignedBigInteger::from_base(16, "-fffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv); - EXPECT_EQUAL_TO(min_value_in_bigint, double_min_value); - EXPECT_LESS_THAN(min_value_minus_one, double_min_value); - EXPECT_GREATER_THAN(min_value_plus_one, double_min_value); - EXPECT_GREATER_THAN(above_min_value_in_bigint, double_min_value); + EXPECT_EQUAL_TO(min_value_in_bigint, double_min_value); + EXPECT_LESS_THAN(min_value_minus_one, double_min_value); + EXPECT_GREATER_THAN(min_value_plus_one, double_min_value); + EXPECT_GREATER_THAN(above_min_value_in_bigint, double_min_value); - EXPECT_LESS_THAN(min_value_in_bigint, double_above_min_value); - EXPECT_LESS_THAN(min_value_minus_one, double_above_min_value); - EXPECT_LESS_THAN(min_value_plus_one, double_above_min_value); - EXPECT_EQUAL_TO(above_min_value_in_bigint, double_above_min_value); - } + EXPECT_LESS_THAN(min_value_in_bigint, double_above_min_value); + EXPECT_LESS_THAN(min_value_minus_one, double_above_min_value); + EXPECT_LESS_THAN(min_value_plus_one, double_above_min_value); + EXPECT_EQUAL_TO(above_min_value_in_bigint, double_above_min_value); +} - { - double just_above_255 = bit_cast(0x406fe00000000001ULL); - double just_below_255 = bit_cast(0x406fdfffffffffffULL); - double double_255 = 255.0; - Crypto::SignedBigInteger bigint_255 { 255 }; +{ + double just_above_255 = bit_cast(0x406fe00000000001ULL); + double just_below_255 = bit_cast(0x406fdfffffffffffULL); + double double_255 = 255.0; + Crypto::SignedBigInteger 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); - } + 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 } +TEST_CASE(to_double) +{ +#define EXPECT_TO_EQUAL_DOUBLE(bigint, double_value) \ + EXPECT_EQ((bigint).to_double(), double_value) + + EXPECT_TO_EQUAL_DOUBLE(Crypto::UnsignedBigInteger(0), 0.0); + // Make sure we don't get negative zero! + EXPECT_EQ(signbit(Crypto::UnsignedBigInteger(0).to_double()), 0); + { + Crypto::SignedBigInteger zero { 0 }; + + EXPECT(!zero.is_negative()); + EXPECT_TO_EQUAL_DOUBLE(zero, 0.0); + EXPECT_EQ(signbit(zero.to_double()), 0); + + zero.negate(); + + EXPECT(!zero.is_negative()); + EXPECT_TO_EQUAL_DOUBLE(zero, 0.0); + EXPECT_EQ(signbit(zero.to_double()), 0); + } + + EXPECT_TO_EQUAL_DOUBLE(Crypto::UnsignedBigInteger(9682), 9682.0); + EXPECT_TO_EQUAL_DOUBLE(Crypto::SignedBigInteger(-9660), -9660.0); + + double double_max_value = NumericLimits::max(); + double infinity = INFINITY; + + EXPECT_TO_EQUAL_DOUBLE( + Crypto::UnsignedBigInteger::from_base(16, "fffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv), + double_max_value); + + EXPECT_TO_EQUAL_DOUBLE( + Crypto::UnsignedBigInteger::from_base(16, "ffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv), + double_max_value); + + EXPECT_TO_EQUAL_DOUBLE( + Crypto::UnsignedBigInteger::from_base(16, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"sv), + double_max_value); + + EXPECT_TO_EQUAL_DOUBLE( + Crypto::UnsignedBigInteger::from_base(16, "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv), + infinity); + + EXPECT_TO_EQUAL_DOUBLE( + Crypto::SignedBigInteger::from_base(16, "-fffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv), + -double_max_value); + + EXPECT_TO_EQUAL_DOUBLE( + Crypto::SignedBigInteger::from_base(16, "-ffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv), + -double_max_value); + + EXPECT_TO_EQUAL_DOUBLE( + Crypto::SignedBigInteger::from_base(16, "-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"sv), + -double_max_value); + + EXPECT_TO_EQUAL_DOUBLE( + Crypto::SignedBigInteger::from_base(16, "-10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"sv), + -infinity); + + EXPECT_TO_EQUAL_DOUBLE( + Crypto::UnsignedBigInteger::from_base(16, "ffffffffffffffff"sv), + 18446744073709549568.0); + + EXPECT_TO_EQUAL_DOUBLE( + Crypto::UnsignedBigInteger::from_base(16, "fffffffffffff800"sv), + 18446744073709549568.0); + + EXPECT_TO_EQUAL_DOUBLE( + Crypto::UnsignedBigInteger::from_base(16, "fffffffffffff8ff"sv), + 18446744073709549568.0); + + EXPECT_TO_EQUAL_DOUBLE(Crypto::SignedBigInteger::from_base(10, "1234567890123456789"sv), + 1234567890123456800.0); + + EXPECT_TO_EQUAL_DOUBLE(Crypto::SignedBigInteger::from_base(10, "2345678901234567890"sv), + 2345678901234567680.0); + + EXPECT_EQ(1234567890123456800.0, 1234567890123456768.0); + +#undef EXPECT_TO_EQUAL_DOUBLE +} + namespace AK { template<> diff --git a/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp b/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp index 48e56970a4..c4a49190e6 100644 --- a/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp +++ b/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp @@ -70,6 +70,8 @@ double SignedBigInteger::to_double() const double unsigned_value = m_unsigned_data.to_double(); if (!m_sign) return unsigned_value; + + VERIFY(!is_zero()); return -unsigned_value; } diff --git a/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp b/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp index 23fcd2dde9..d9b8e7566d 100644 --- a/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp +++ b/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp @@ -115,8 +115,95 @@ u64 UnsignedBigInteger::to_u64() const double UnsignedBigInteger::to_double() const { - // FIXME: I am naive - return static_cast(to_u64()); + // NOTE: This function rounds toward zero! + // FIXME: Which is not exactly what we should do for JS when converting to number: + // See: https://tc39.es/ecma262/#sec-number-constructor-number-value + // Which has step 1.b If Type(prim) is BigInt, let n be 𝔽(ℝ(prim)). + // Which then references: https://tc39.es/ecma262/#sec-ecmascript-language-types-number-type + // Which is equivalent to (This procedure corresponds exactly to the behaviour of the IEEE 754-2019 roundTiesToEven mode.) + + auto highest_bit = one_based_index_of_highest_set_bit(); + if (highest_bit == 0) + return 0; + --highest_bit; + + // Simple case if less than 2^53 since those number are all exactly representable in doubles + if (highest_bit < 53) + return static_cast(to_u64()); + + constexpr u64 mantissa_size = 52; + constexpr u64 exponent_size = 11; + constexpr auto exponent_bias = (1 << (exponent_size - 1)) - 1; + + // If it uses too many bit to represent in a double return infinity + if (highest_bit > exponent_bias) + return __builtin_huge_val(); + + // Otherwise we have to take the top 53 bits, use those as the mantissa, + // and the amount of bits as the exponent. Note that the mantissa has an implicit top bit of 1 + // 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)); + constexpr auto bits_in_u64 = 64; + static_assert(bits_in_u64 > mantissa_size + 1); + + auto bits_to_read = min(mantissa_size + 1, highest_bit); + + auto last_word_index = trimmed_length(); + VERIFY(last_word_index > 0); + + // Note that highest bit is 0-indexed at this point. + auto highest_bit_index_in_top_word = highest_bit % BITS_IN_WORD; + + // Shift initial word until highest bit is just beyond top of u64. + u64 mantissa = static_cast(m_words[last_word_index - 1]) << (bits_in_u64 - highest_bit_index_in_top_word); + + auto bits_written = highest_bit_index_in_top_word; + + --last_word_index; + + if (bits_written < bits_to_read && last_word_index > 0) { + // Second word can always just cleanly be shifted upto the final bit of the first word + // since the first has at most BIT_IN_WORD - 1, 31 + u64 next_word = m_words[last_word_index - 1]; + VERIFY((mantissa & (next_word << (bits_in_u64 - bits_written - BITS_IN_WORD))) == 0); + mantissa |= next_word << (bits_in_u64 - bits_written - BITS_IN_WORD); + bits_written += BITS_IN_WORD; + --last_word_index; + + if (bits_written < bits_to_read && last_word_index > 0) { + // The final word has to be shifted down first to discard any excess bits. + u64 final_word = m_words[last_word_index - 1]; + + auto bits_to_write = bits_to_read - bits_written; + + final_word >>= (BITS_IN_WORD - bits_to_write); + + // Then move the bits right up to the lowest bits of the second word + VERIFY((mantissa & (final_word << (bits_in_u64 - bits_written - bits_to_write))) == 0); + mantissa |= final_word << (bits_in_u64 - bits_written - BITS_IN_WORD); + } + } + + // Now the mantissa should be complete so shift it down + mantissa >>= bits_in_u64 - mantissa_size; + + union FloatExtractor { + 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; + + VERIFY((mantissa & 0xfff0000000000000) == 0); + extractor.mantissa = mantissa; + + return extractor.double_value; } void UnsignedBigInteger::set_to_0()