Implement multiplication and part of assignment from pow of 10

This commit is contained in:
Victor Zverovich 2019-09-08 08:09:24 -07:00
parent 0887887e23
commit b3bf665764
2 changed files with 65 additions and 4 deletions

View File

@ -473,14 +473,14 @@ class bigint {
// A bigint is stored as an array of bigits (big digits), with bigit at index
// 0 being the least significant one.
using bigit = uint32_t;
using double_bigit = uint64_t;
basic_memory_buffer<bigit> bigits_;
static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
friend struct formatter<bigint>;
public:
explicit bigint(uint64_t n) {
void assign(uint64_t n) {
int num_bigits = bits<uint64_t>::value / bigit_bits;
bigits_.resize(num_bigits);
for (int i = 0; i < num_bigits; ++i) {
@ -489,9 +489,37 @@ class bigint {
}
}
public:
bigint() {}
explicit bigint(uint64_t n) { assign(n); }
// Assigns pow(10, exp) to this bigint.
void assign_pow10(int exp) {
assert(exp >= 0);
if (exp == 0) return assign(1);
// Find the top bit.
int bitmask = 1;
while (exp >= bitmask) bitmask <<= 1;
bitmask >>= 1;
// pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
// repeated squaring and multiplication.
assign(5);
bitmask >>= 1;
while (bitmask != 0) {
square();
if ((exp & bitmask) != 0) *this *= 5;
bitmask >>= 1;
}
*this <<= exp; // Multiply by pow(2, exp) by shifting.
}
bigint(const bigint&) = delete;
void operator=(const bigint&) = delete;
void square() {
// TODO
}
bigint& operator<<=(int shift) {
assert(shift >= 0 && shift < bigit_bits);
bigit carry = 0;
@ -503,6 +531,23 @@ class bigint {
if (carry != 0) bigits_.push_back(carry);
return *this;
}
bigint& operator*=(uint32_t value) {
assert(value > 0);
// Verify that the computation doesn't overflow.
constexpr double_bigit max32 = (std::numeric_limits<bigit>::max)();
constexpr double_bigit max64 = (std::numeric_limits<double_bigit>::max)();
static_assert(max32 * max32 <= max64 - max32, "");
bigit carry = 0;
const double_bigit wide_value = value;
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
double_bigit result = bigits_[i] * wide_value + carry;
bigits_[i] = static_cast<bigit>(result);
carry = static_cast<bigit>(result >> bigit_bits);
}
if (carry != 0) bigits_.push_back(carry);
return *this;
}
};
enum round_direction { unknown, up, down };
@ -725,8 +770,9 @@ template <int GRISU_VERSION> struct grisu_shortest_handler {
}
};
FMT_API void fallback_format(const fp& value) {
FMT_API void fallback_format(const fp& value, int exp10) {
(void)value; // TODO
(void)exp10;
}
template <typename Double,
@ -782,7 +828,7 @@ FMT_API bool grisu_format(Double value, buffer<char>& buf, int precision,
result = grisu_gen_digits(upper, upper.f - lower.f, exp, handler);
size = handler.size;
if (result == digits::error) {
fallback_format(fp_value);
fallback_format(fp_value, exp - cached_exp10);
return false;
}
} else {

View File

@ -29,6 +29,7 @@ static_assert(!std::is_copy_constructible<bigint>::value, "");
static_assert(!std::is_copy_assignable<bigint>::value, "");
TEST(BigIntTest, Construct) {
EXPECT_EQ("", fmt::format("{}", bigint()));
EXPECT_EQ("42", fmt::format("{}", bigint(0x42)));
EXPECT_EQ("123456789abcedf0", fmt::format("{}", bigint(0x123456789abcedf0)));
}
@ -43,6 +44,20 @@ TEST(BigIntTest, ShiftLeft) {
EXPECT_EQ("108000000", fmt::format("{}", n));
}
TEST(BigIntTest, Multiply) {
bigint n(0x42);
n *= 1;
EXPECT_EQ("42", fmt::format("{}", n));
n *= 2;
EXPECT_EQ("84", fmt::format("{}", n));
n *= 0x12345678;
EXPECT_EQ("962fc95e0", fmt::format("{}", n));
auto max = (std::numeric_limits<uint32_t>::max)();
bigint bigmax(max);
bigmax *= max;
EXPECT_EQ("fffffffe00000001", fmt::format("{}", bigmax));
}
template <bool is_iec559> void test_construct_from_double() {
fmt::print("warning: double is not IEC559, skipping FP tests\n");
}