forked from fmtlib/fmt
Implement add_compare
This commit is contained in:
@ -534,17 +534,15 @@ class bigint {
|
|||||||
int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
|
int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
|
||||||
if (num_lhs_bigits != num_rhs_bigits)
|
if (num_lhs_bigits != num_rhs_bigits)
|
||||||
return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
|
return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
|
||||||
int lhs_bigit_index = static_cast<int>(lhs.bigits_.size()) - 1;
|
int i = static_cast<int>(lhs.bigits_.size()) - 1;
|
||||||
int rhs_bigit_index = static_cast<int>(rhs.bigits_.size()) - 1;
|
int j = static_cast<int>(rhs.bigits_.size()) - 1;
|
||||||
int end = lhs_bigit_index - rhs_bigit_index;
|
int end = i - j;
|
||||||
if (end < 0) end = 0;
|
if (end < 0) end = 0;
|
||||||
for (; lhs_bigit_index >= end; --lhs_bigit_index, --rhs_bigit_index) {
|
for (; i >= end; --i, --j) {
|
||||||
bigit lhs_bigit = lhs.bigits_[lhs_bigit_index];
|
bigit lhs_bigit = lhs.bigits_[i], rhs_bigit = rhs.bigits_[j];
|
||||||
bigit rhs_bigit = rhs.bigits_[rhs_bigit_index];
|
|
||||||
if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
|
if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
|
||||||
}
|
}
|
||||||
if (lhs_bigit_index != rhs_bigit_index)
|
if (i != j) return i > j ? 1 : -1;
|
||||||
return lhs_bigit_index > rhs_bigit_index ? 1 : -1;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -605,6 +603,30 @@ class bigint {
|
|||||||
return compare(lhs, rhs) >= 0;
|
return compare(lhs, rhs) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns compare(lhs1 + lhs2, rhs).
|
||||||
|
friend int add_compare(const bigint& lhs1, const bigint& lhs2,
|
||||||
|
const bigint& rhs) {
|
||||||
|
int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits());
|
||||||
|
int num_rhs_bigits = rhs.num_bigits();
|
||||||
|
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
|
||||||
|
if (max_lhs_bigits > num_rhs_bigits) return 1;
|
||||||
|
auto get_bigit = [](const bigint& n, int i) -> bigit {
|
||||||
|
return i >= n.exp_ && i < n.num_bigits() ? n.bigits_[i - n.exp_] : 0;
|
||||||
|
};
|
||||||
|
double_bigit borrow = 0;
|
||||||
|
int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_);
|
||||||
|
for (int i = num_rhs_bigits - 1; i >= min_exp; --i) {
|
||||||
|
double_bigit sum =
|
||||||
|
static_cast<double_bigit>(get_bigit(lhs1, i)) + get_bigit(lhs2, i);
|
||||||
|
bigit rhs_bigit = get_bigit(rhs, i);
|
||||||
|
if (sum > rhs_bigit + borrow) return 1;
|
||||||
|
borrow = rhs_bigit + borrow - sum;
|
||||||
|
if (borrow > 1) return -1;
|
||||||
|
borrow <<= bigit_bits;
|
||||||
|
}
|
||||||
|
return borrow != 0 ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Assigns pow(10, exp) to this bigint.
|
// Assigns pow(10, exp) to this bigint.
|
||||||
void assign_pow10(int exp) {
|
void assign_pow10(int exp) {
|
||||||
assert(exp >= 0);
|
assert(exp >= 0);
|
||||||
@ -900,17 +922,17 @@ template <int GRISU_VERSION> struct grisu_shortest_handler {
|
|||||||
|
|
||||||
// Format value using a variation of the Fixed-Precision Positive Floating-Point
|
// Format value using a variation of the Fixed-Precision Positive Floating-Point
|
||||||
// Printout ((FPP)^2) algorithm by Steele & White.
|
// Printout ((FPP)^2) algorithm by Steele & White.
|
||||||
template <typename Double> FMT_FUNC void fallback_format(Double v, int exp10) {
|
template <typename Double>
|
||||||
(void)exp10;
|
FMT_FUNC void fallback_format(Double v, buffer<char>& buf, int& exp10) {
|
||||||
fp fp_value(v);
|
fp fp_value(v);
|
||||||
// Shift to account for unequal gaps when lower boundary is 2 times closer.
|
// Shift to account for unequal gaps when lower boundary is 2 times closer.
|
||||||
// TODO: handle denormals
|
// TODO: handle denormals
|
||||||
int shift = fp_value.f == 1 ? 1 : 0;
|
int shift = fp_value.f == 1 ? 1 : 0;
|
||||||
// Shift value and pow10 by an extra bit to make lower and upper which are
|
// Shift value and pow10 by an extra bit to make lower and upper which are
|
||||||
// half ulp integers. This eliminates multiplication by 2 during later
|
// half ulp integers. This eliminates multiplication by 2 during later
|
||||||
// computations in (FPP)^2.
|
// computations.
|
||||||
bigint value(fp_value.f << (shift + 1)); // R in (FPP)^2.
|
bigint value(fp_value.f << (shift + 1)); // 2 * R in (FPP)^2.
|
||||||
bigint pow10(1 << (shift + 1)); // S in (FPP)^2.
|
bigint pow10(1 << (shift + 1)); // 2 * S in (FPP)^2.
|
||||||
bigint lower(1); // M^- in (FPP)^2.
|
bigint lower(1); // M^- in (FPP)^2.
|
||||||
bigint upper(1 << shift); // M^+ in (FPP)^2.
|
bigint upper(1 << shift); // M^+ in (FPP)^2.
|
||||||
if (fp_value.e >= 0) {
|
if (fp_value.e >= 0) {
|
||||||
@ -921,16 +943,30 @@ template <typename Double> FMT_FUNC void fallback_format(Double v, int exp10) {
|
|||||||
pow10 <<= -fp_value.e;
|
pow10 <<= -fp_value.e;
|
||||||
// TODO: fixup
|
// TODO: fixup
|
||||||
}
|
}
|
||||||
// fp_value = value / pow10.
|
// fp_value == value / pow10.
|
||||||
while (value /* + upper */ >= pow10) pow10 *= 10;
|
int exp = 0;
|
||||||
do {
|
while (add_compare(value, upper, pow10) >= 0) {
|
||||||
|
pow10 *= 10;
|
||||||
|
++exp;
|
||||||
|
}
|
||||||
|
int num_digits = 0;
|
||||||
|
char* data = buf.data();
|
||||||
|
for (;;) {
|
||||||
value *= 10;
|
value *= 10;
|
||||||
int digit = value.divmod_assign(pow10);
|
int digit = value.divmod_assign(pow10);
|
||||||
(void)digit;
|
|
||||||
lower *= 10;
|
lower *= 10;
|
||||||
upper *= 10;
|
upper *= 10;
|
||||||
} while (value >= lower && value <= pow10 /* - upper */);
|
bool low = value < lower;
|
||||||
// TODO
|
bool high = add_compare(value, upper, pow10) > 0; // value + upper > pow10
|
||||||
|
if (low || high) {
|
||||||
|
if (!low) ++digit;
|
||||||
|
data[num_digits++] = static_cast<char>('0' + digit);
|
||||||
|
buf.resize(num_digits);
|
||||||
|
exp10 = exp - num_digits;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data[num_digits++] = static_cast<char>('0' + digit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Double,
|
template <typename Double,
|
||||||
@ -986,8 +1022,9 @@ FMT_API bool grisu_format(Double value, buffer<char>& buf, int precision,
|
|||||||
result = grisu_gen_digits(upper, upper.f - lower.f, exp, handler);
|
result = grisu_gen_digits(upper, upper.f - lower.f, exp, handler);
|
||||||
size = handler.size;
|
size = handler.size;
|
||||||
if (result == digits::error) {
|
if (result == digits::error) {
|
||||||
fallback_format(value, exp - cached_exp10);
|
exp -= cached_exp10;
|
||||||
return false;
|
fallback_format(value, buf, exp);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
++lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
|
++lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
|
||||||
|
@ -95,6 +95,32 @@ TEST(BigIntTest, GreaterEqual) {
|
|||||||
EXPECT_TRUE(n4 >= n2);
|
EXPECT_TRUE(n4 >= n2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(BigIntTest, AddCompare) {
|
||||||
|
EXPECT_LT(
|
||||||
|
add_compare(bigint(0xffffffff), bigint(0xffffffff), bigint(1) <<= 64), 0);
|
||||||
|
EXPECT_LT(add_compare(bigint(1) <<= 32, bigint(1), bigint(1) <<= 96), 0);
|
||||||
|
EXPECT_GT(add_compare(bigint(1) <<= 32, bigint(0), bigint(0xffffffff)), 0);
|
||||||
|
EXPECT_GT(add_compare(bigint(0), bigint(1) <<= 32, bigint(0xffffffff)), 0);
|
||||||
|
EXPECT_GT(add_compare(bigint(42), bigint(1), bigint(42)), 0);
|
||||||
|
EXPECT_GT(add_compare(bigint(0xffffffff), bigint(1), bigint(0xffffffff)), 0);
|
||||||
|
EXPECT_LT(add_compare(bigint(10), bigint(10), bigint(22)), 0);
|
||||||
|
EXPECT_LT(add_compare(bigint(0x100000010), bigint(0x100000010),
|
||||||
|
bigint(0x300000010)),
|
||||||
|
0);
|
||||||
|
EXPECT_GT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
|
||||||
|
bigint(0x300000000)),
|
||||||
|
0);
|
||||||
|
EXPECT_EQ(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
|
||||||
|
bigint(0x300000001)),
|
||||||
|
0);
|
||||||
|
EXPECT_LT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
|
||||||
|
bigint(0x300000002)),
|
||||||
|
0);
|
||||||
|
EXPECT_LT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
|
||||||
|
bigint(0x300000003)),
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(BigIntTest, ShiftLeft) {
|
TEST(BigIntTest, ShiftLeft) {
|
||||||
bigint n(0x42);
|
bigint n(0x42);
|
||||||
n <<= 0;
|
n <<= 0;
|
||||||
|
Reference in New Issue
Block a user