mirror of
https://github.com/scylladb/scylladb.git
synced 2026-05-13 11:22:01 +00:00
Merge "Fix handling of decimals with negative scales" from Rafael
" Before this series scylla would effectively infinite loop when, for example, casting a decimal with a negative scale to float. Fixes #6720 " * 'espindola/fix-decimal-issue' of https://github.com/espindola/scylla: big_decimal: Add a test for a corner case big_decimal: Correctly handle negative scales big_decimal: Add a as_rational member function big_decimal: Move constructors out of line
This commit is contained in:
@@ -88,16 +88,13 @@ static data_value castas_fctn_simple(data_value from) {
|
||||
template<typename ToType>
|
||||
static data_value castas_fctn_from_decimal_to_float(data_value from) {
|
||||
auto val_from = value_cast<big_decimal>(from);
|
||||
boost::multiprecision::cpp_int ten(10);
|
||||
boost::multiprecision::cpp_rational r = val_from.unscaled_value();
|
||||
r /= boost::multiprecision::pow(ten, val_from.scale());
|
||||
return static_cast<ToType>(r);
|
||||
return static_cast<ToType>(val_from.as_rational());
|
||||
}
|
||||
|
||||
static utils::multiprecision_int from_decimal_to_cppint(const data_value& from) {
|
||||
const auto& val_from = value_cast<big_decimal>(from);
|
||||
boost::multiprecision::cpp_int ten(10);
|
||||
return boost::multiprecision::cpp_int(val_from.unscaled_value() / boost::multiprecision::pow(ten, val_from.scale()));
|
||||
auto r = val_from.as_rational();
|
||||
return utils::multiprecision_int(numerator(r)/denominator(r));
|
||||
}
|
||||
|
||||
template<typename ToType>
|
||||
|
||||
10
lua.cc
10
lua.cc
@@ -262,14 +262,12 @@ static auto visit_lua_raw_value(lua_State* l, int index, Func&& f) {
|
||||
|
||||
template <typename Func>
|
||||
static auto visit_decimal(const big_decimal &v, Func&& f) {
|
||||
boost::multiprecision::cpp_int ten(10);
|
||||
const auto& dividend = v.unscaled_value();
|
||||
auto divisor = boost::multiprecision::pow(ten, v.scale());
|
||||
boost::multiprecision::cpp_rational r = v.as_rational();
|
||||
const boost::multiprecision::cpp_int& dividend = numerator(r);
|
||||
const boost::multiprecision::cpp_int& divisor = denominator(r);
|
||||
if (dividend % divisor == 0) {
|
||||
return f(utils::multiprecision_int(boost::multiprecision::cpp_int(dividend/divisor)));
|
||||
return f(utils::multiprecision_int(dividend/divisor));
|
||||
}
|
||||
boost::multiprecision::cpp_rational r = dividend;
|
||||
r /= divisor;
|
||||
return f(r.convert_to<double>());
|
||||
}
|
||||
|
||||
|
||||
@@ -157,6 +157,13 @@ BOOST_AUTO_TEST_CASE(test_big_decimal_div) {
|
||||
test_div("-0.25", 10, "-0.02");
|
||||
test_div("-0.26", 10, "-0.03");
|
||||
test_div("-10E10", 3, "-3E10");
|
||||
|
||||
// Document a small oddity, 1e1 has -1 decimal places, so dividing
|
||||
// it by 2 produces 0. This is not the behavior in cassandra, but
|
||||
// scylla doesn't expose arithmetic operations, so this doesn't
|
||||
// seem to be visible from CQL.
|
||||
test_div("10", 2, "5");
|
||||
test_div("1e1", 2, "0e1");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_big_decimal_assignadd) {
|
||||
|
||||
@@ -142,6 +142,19 @@ SEASTAR_TEST_CASE(test_decimal_to_bigint) {
|
||||
});
|
||||
}
|
||||
|
||||
SEASTAR_TEST_CASE(test_decimal_to_float) {
|
||||
return do_with_cql_env_thread([&](auto& e) {
|
||||
e.execute_cql("CREATE TABLE test (key text primary key, value decimal)").get();
|
||||
e.execute_cql("INSERT INTO test (key, value) VALUES ('k1', 10)").get();
|
||||
e.execute_cql("INSERT INTO test (key, value) VALUES ('k2', 1e1)").get();
|
||||
auto v = e.execute_cql("SELECT key, CAST(value as float) from test").get0();
|
||||
assert_that(v).is_rows().with_rows_ignore_order({
|
||||
{{serialized("k1")}, {serialized(float(10))}},
|
||||
{{serialized("k2")}, {serialized(float(10))}},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
SEASTAR_TEST_CASE(test_varint_to_bigint) {
|
||||
return do_with_cql_env_thread([&](auto& e) {
|
||||
e.execute_cql("CREATE TABLE test (key text primary key, value varint)").get();
|
||||
|
||||
@@ -36,6 +36,9 @@ uint64_t from_varint_to_integer(const utils::multiprecision_int& varint) {
|
||||
return static_cast<uint64_t>(~static_cast<uint64_t>(0) & boost::multiprecision::cpp_int(varint));
|
||||
}
|
||||
|
||||
big_decimal::big_decimal() : big_decimal(0, 0) {}
|
||||
big_decimal::big_decimal(int32_t scale, boost::multiprecision::cpp_int unscaled_value)
|
||||
: _scale(scale), _unscaled_value(std::move(unscaled_value)) {}
|
||||
|
||||
big_decimal::big_decimal(sstring_view text)
|
||||
{
|
||||
@@ -82,6 +85,20 @@ big_decimal::big_decimal(sstring_view text)
|
||||
_scale += fraction.size();
|
||||
}
|
||||
|
||||
boost::multiprecision::cpp_rational big_decimal::as_rational() const {
|
||||
boost::multiprecision::cpp_int ten(10);
|
||||
auto unscaled_value = static_cast<const boost::multiprecision::cpp_int&>(_unscaled_value);
|
||||
boost::multiprecision::cpp_rational r = unscaled_value;
|
||||
int32_t abs_scale = std::abs(_scale);
|
||||
auto pow = boost::multiprecision::pow(ten, abs_scale);
|
||||
if (_scale < 0) {
|
||||
r *= pow;
|
||||
} else {
|
||||
r /= pow;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
sstring big_decimal::to_string() const
|
||||
{
|
||||
if (!_unscaled_value) {
|
||||
|
||||
@@ -39,13 +39,12 @@ public:
|
||||
};
|
||||
|
||||
explicit big_decimal(sstring_view text);
|
||||
big_decimal() : big_decimal(0, 0) {}
|
||||
big_decimal(int32_t scale, boost::multiprecision::cpp_int unscaled_value)
|
||||
: _scale(scale), _unscaled_value(unscaled_value)
|
||||
{ }
|
||||
big_decimal();
|
||||
big_decimal(int32_t scale, boost::multiprecision::cpp_int unscaled_value);
|
||||
|
||||
int32_t scale() const { return _scale; }
|
||||
const boost::multiprecision::cpp_int& unscaled_value() const { return _unscaled_value; }
|
||||
boost::multiprecision::cpp_rational as_rational() const;
|
||||
|
||||
sstring to_string() const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user