1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

cleanup: remove explicit rounding before decimal2longlong

embed rounding into decimal2longlong instead.
this avoid carry propagation loop on rounding.
and allows decimal2longlong detect truncation correctly
This commit is contained in:
Sergei Golubchik
2025-12-31 17:19:38 +01:00
parent 473a6cea79
commit 89c933d61c
7 changed files with 67 additions and 29 deletions

View File

@@ -46,9 +46,9 @@ int decimal2string(const decimal_t *from, char *to, int *to_len,
decimal_digits_t fixed_precision,
decimal_digits_t fixed_decimals,
char filler);
int decimal2ulonglong(const decimal_t *from, ulonglong *to);
int decimal2ulonglong(const decimal_t *from, ulonglong *to, decimal_round_mode);
int ulonglong2decimal(ulonglong from, decimal_t *to);
int decimal2longlong(const decimal_t *from, longlong *to);
int decimal2longlong(const decimal_t *from, longlong *to, decimal_round_mode);
int longlong2decimal(longlong from, decimal_t *to);
int decimal2double(const decimal_t *from, double *to);
int double2decimal(double from, decimal_t *to);

View File

@@ -4041,7 +4041,7 @@ mariadb_dyncol_val_long(longlong *ll, DYNAMIC_COLUMN_VALUE *val)
break;
}
case DYN_COL_DECIMAL:
if (decimal2longlong(&val->x.decimal.value, ll) != E_DEC_OK)
if (decimal2longlong(&val->x.decimal.value, ll,TRUNCATE) != E_DEC_OK)
rc= ER_DYNCOL_TRUNCATED;
break;
case DYN_COL_DATETIME:

View File

@@ -349,11 +349,8 @@ int my_decimal2int(uint mask, const decimal_t *d, bool is_unsigned,
longlong *l, decimal_round_mode round_type)
{
int res;
my_decimal rounded;
/* decimal_round can return only E_DEC_TRUNCATED */
decimal_round(d, &rounded, 0, round_type);
res= is_unsigned ? decimal2ulonglong(&rounded, (ulonglong *) l)
: decimal2longlong(&rounded, l);
res= is_unsigned ? decimal2ulonglong(d, (ulonglong *) l, round_type)
: decimal2longlong(d, l, round_type);
if (res & mask)
{
char buff[DECIMAL_MAX_STR_LENGTH];

View File

@@ -481,11 +481,10 @@ struct Master_info_file: Info_file
return true; // ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE
overprecise= decimal.frac > 3;
// decomposed from my_decimal2int() to reduce a bit of computations
auto rounded= my_decimal(), product= my_decimal();
auto product= my_decimal();
bool unexpected_error=
decimal_round(&decimal, &rounded, 3, HALF_UP) ||
decimal_mul(&rounded, &THOUSAND, &product) ||
decimal2ulonglong(&product, &decimal_out);
decimal_mul(&decimal, &THOUSAND, &product) ||
(decimal2ulonglong(&product, &decimal_out, HALF_UP) > E_DEC_TRUNCATED);
DBUG_ASSERT(!unexpected_error);
if (unexpected_error)
return true;

View File

@@ -2828,7 +2828,7 @@ static inline int part_val_int(Item *item_expr, longlong *result)
my_decimal buf;
my_decimal *val= item_expr->val_decimal(&buf);
if (val && my_decimal2int(E_DEC_FATAL_ERROR, val, item_expr->unsigned_flag,
result, FLOOR) != E_DEC_OK)
result, FLOOR) & E_DEC_FATAL_ERROR)
return true;
break;
}

View File

@@ -1122,11 +1122,11 @@ int longlong2decimal(longlong from, decimal_t *to)
return ull2dec(from, to);
}
int decimal2ulonglong(const decimal_t *from, ulonglong *to)
int decimal2ulonglong(const decimal_t *from, ulonglong *to, decimal_round_mode mode)
{
dec1 *buf=from->buf;
ulonglong x=0;
int intg, frac;
int intg, frac= from->frac;
if (from->sign)
{
@@ -1152,18 +1152,38 @@ int decimal2ulonglong(const decimal_t *from, ulonglong *to)
x=x*DIG_BASE + *buf++;
}
*to=x;
for (frac=from->frac; unlikely(frac > 0); frac-=DIG_PER_DEC1)
if (*buf++)
return E_DEC_TRUNCATED;
return E_DEC_OK;
*to= x;
if (frac > 0)
{
int carry= 0;
for (dec1 *b= buf; frac > 0 && !*b++; frac-=DIG_PER_DEC1) /* no-op */;
switch (mode) {
case HALF_UP: carry= *buf >= 5*DIG_MASK; break;
case HALF_EVEN: carry= *buf >= (6-(int)(x%2))*DIG_MASK; break;
case CEILING: carry= frac > 0; break;
case FLOOR: carry= 0; break;
case TRUNCATE: carry= 0; break;
default: DBUG_ASSERT(0);
}
if (carry)
{
if (x == ULONGLONG_MAX)
return E_DEC_OVERFLOW;
(*to)++;
}
}
return frac > 0 ? E_DEC_TRUNCATED : E_DEC_OK;
}
int decimal2longlong(const decimal_t *from, longlong *to)
int decimal2longlong(const decimal_t *from, longlong *to, decimal_round_mode mode)
{
dec1 *buf=from->buf;
longlong x=0;
int intg, frac;
int intg, frac= from->frac;
for (intg=from->intg; intg > 0; intg-=DIG_PER_DEC1)
{
@@ -1191,6 +1211,31 @@ int decimal2longlong(const decimal_t *from, longlong *to)
x=x*DIG_BASE - *buf++;
}
if (frac > 0)
{
int carry= 0;
for (dec1 *b= buf; frac > 0 && !*b++; frac-=DIG_PER_DEC1) /* no-op */;
switch (mode) {
case HALF_UP: carry= *buf >= 5*DIG_MASK; break;
case HALF_EVEN: carry= *buf >= (6-x%2)*DIG_MASK; break;
case CEILING: carry= frac > 0 && !from->sign; break;
case FLOOR: carry= frac > 0 && from->sign; break;
case TRUNCATE: carry= 0; break;
default: DBUG_ASSERT(0);
}
if (carry)
{
if (x == LONGLONG_MIN)
{
*to= from->sign ? LONGLONG_MIN : LONGLONG_MAX;
return E_DEC_OVERFLOW;
}
x--;
}
}
/* boundary case: 9223372036854775808 */
if (unlikely(from->sign==0 && x == LONGLONG_MIN))
{
@@ -1199,10 +1244,7 @@ int decimal2longlong(const decimal_t *from, longlong *to)
}
*to=from->sign ? x : -x;
for (frac=from->frac; unlikely(frac > 0); frac-=DIG_PER_DEC1)
if (*buf++)
return E_DEC_TRUNCATED;
return E_DEC_OK;
return frac > 0 ? E_DEC_TRUNCATED : E_DEC_OK;
}
/*

View File

@@ -43,9 +43,9 @@ test_copy_and_compare()
ok(my_decimal_cmp(&d3, &d1) == 0,"Pass");
ulonglong val1, val2, val3;
ok(decimal2ulonglong(&d1, &val1) == 0, "Pass");
ok(decimal2ulonglong(&d2, &val2) == 0,"Pass");
ok(decimal2ulonglong(&d3, &val3) == 0,"Pass");
ok(decimal2ulonglong(&d1, &val1, TRUNCATE) == 0, "Pass");
ok(decimal2ulonglong(&d2, &val2, TRUNCATE) == 0,"Pass");
ok(decimal2ulonglong(&d3, &val3, TRUNCATE) == 0,"Pass");
ok(val == val1,"Pass");
ok(val == val2,"Pass");