Commit 1f7aa03e authored by serg@serg.mylan's avatar serg@serg.mylan

new api per hf request:

  string2decimal_fixed
  decimal_round(from, to)
  decimal_make_zero
  decimal_string_size
  decimal_neg
parent 4375e599
...@@ -30,6 +30,7 @@ typedef struct st_decimal { ...@@ -30,6 +30,7 @@ typedef struct st_decimal {
int decimal2string(decimal *from, char *to, int *to_len); int decimal2string(decimal *from, char *to, int *to_len);
int string2decimal(char *from, decimal *to, char **end); int string2decimal(char *from, decimal *to, char **end);
int string2decimal_fixed(char *from, decimal *to, char **end);
int decimal2ulonglong(decimal *from, ulonglong *to); int decimal2ulonglong(decimal *from, ulonglong *to);
int ulonglong2decimal(ulonglong from, decimal *to); int ulonglong2decimal(ulonglong from, decimal *to);
int decimal2longlong(decimal *from, longlong *to); int decimal2longlong(decimal *from, longlong *to);
...@@ -49,14 +50,34 @@ int decimal_cmp(decimal *from1, decimal *from2); ...@@ -49,14 +50,34 @@ int decimal_cmp(decimal *from1, decimal *from2);
int decimal_mul(decimal *from1, decimal *from2, decimal *to); int decimal_mul(decimal *from1, decimal *from2, decimal *to);
int decimal_div(decimal *from1, decimal *from2, decimal *to, int scale_incr); int decimal_div(decimal *from1, decimal *from2, decimal *to, int scale_incr);
int decimal_mod(decimal *from1, decimal *from2, decimal *to); int decimal_mod(decimal *from1, decimal *from2, decimal *to);
int decimal_round(decimal *dec, int new_scale, dec_round_mode mode); int decimal_round(decimal *from, decimal *to, int new_scale, dec_round_mode mode);
/* /*
the following works only on special "zero" decimal, not on any the following works only on special "zero" decimal, not on any
decimal that happen to evaluate to zero decimal that happen to evaluate to zero
*/ */
#define decimal_is_zero(dec) ((dec)->intg1==1 && (dec)->frac1==0 && (dec)->buf[0]==0) #define decimal_is_zero(dec) ((dec)->intg1==1 && (dec)->frac1==0 && (dec)->buf[0]==0)
/* set a decimal to zero */
#define decimal_make_zero(dec) do { \
(dec)->buf[0]=0; \
(dec)->intg=1; \
(dec)->frac=0; \
(dec)->sign=0; \
} while(0)
/*
returns the length of the buffer to hold string representation
of the decimal
*/
#define decimal_string_size(dec) ((dec)->intg + (dec)->frac + ((dec)->frac > 0) + 1)
/* negate a decimal */
#define decimal_neg(dec) do { (dec)->sign=!(dec)->sign; } while(0)
/* /*
conventions: conventions:
......
...@@ -182,13 +182,6 @@ static const int dig2bytes[DIG_PER_DEC1+1]={0, 1, 1, 2, 2, 3, 3, 3, 4, 4}; ...@@ -182,13 +182,6 @@ static const int dig2bytes[DIG_PER_DEC1+1]={0, 1, 1, 2, 2, 3, 3, 3, 4, 4};
(to)=a; \ (to)=a; \
} while(0) } while(0)
#define MAKE_ZERO(dec) do { \
dec->buf[0]=0; \
dec->intg=1; \
dec->frac=0; \
dec->sign=0; \
} while(0)
/* /*
Convert decimal to its printable string representation Convert decimal to its printable string representation
...@@ -291,18 +284,23 @@ int decimal2string(decimal *from, char *to, int *to_len) ...@@ -291,18 +284,23 @@ int decimal2string(decimal *from, char *to, int *to_len)
Convert string to decimal Convert string to decimal
SYNOPSIS SYNOPSIS
string2decimal() str2decl()
from - value to convert from - value to convert
to - decimal where where the result will be stored to - decimal where where the result will be stored
to->buf and to->len must be set. to->buf and to->len must be set.
end - if not NULL, *end will be set to the char where end - if not NULL, *end will be set to the char where
conversion ended conversion ended
fixed - use to->intg, to->frac as limits for input number
NOTE
to->intg and to->frac can be modified even when fixed=1
(but only decreased, in this case)
RETURN VALUE RETURN VALUE
E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW/E_DEC_BAD_NUM; E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW/E_DEC_BAD_NUM/E_DEC_OOM
*/ */
int string2decimal(char *from, decimal *to, char **end) static int str2dec(char *from, decimal *to, char **end, my_bool fixed)
{ {
char *s=from, *s1; char *s=from, *s1;
int i, intg, frac, error, intg1, frac1; int i, intg, frac, error, intg1, frac1;
...@@ -334,14 +332,34 @@ int string2decimal(char *from, decimal *to, char **end) ...@@ -334,14 +332,34 @@ int string2decimal(char *from, decimal *to, char **end)
if (frac+intg == 0) if (frac+intg == 0)
return E_DEC_BAD_NUM; return E_DEC_BAD_NUM;
intg1=ROUND_UP(intg); if (fixed)
frac1=ROUND_UP(frac); {
FIX_INTG_FRAC_ERROR(to->len, intg1, frac1, error); if (frac > to->frac)
if (unlikely(error)) {
error=E_DEC_TRUNCATED;
frac=to->frac;
}
if (intg > to->intg)
{
error=E_DEC_OVERFLOW;
intg=to->intg;
}
intg1=ROUND_UP(intg);
frac1=ROUND_UP(frac);
if (intg1+frac1 > to->len)
return E_DEC_OOM;
}
else
{ {
frac=frac1*DIG_PER_DEC1; intg1=ROUND_UP(intg);
if (error == E_DEC_OVERFLOW) frac1=ROUND_UP(frac);
intg=intg1*DIG_PER_DEC1; FIX_INTG_FRAC_ERROR(to->len, intg1, frac1, error);
if (unlikely(error))
{
frac=frac1*DIG_PER_DEC1;
if (error == E_DEC_OVERFLOW)
intg=intg1*DIG_PER_DEC1;
}
} }
to->intg=intg; to->intg=intg;
to->frac=frac; to->frac=frac;
...@@ -381,6 +399,16 @@ int string2decimal(char *from, decimal *to, char **end) ...@@ -381,6 +399,16 @@ int string2decimal(char *from, decimal *to, char **end)
return error; return error;
} }
int string2decimal(char *from, decimal *to, char **end)
{
return str2dec(from, to, end, 0);
}
int string2decimal_fixed(char *from, decimal *to, char **end)
{
return str2dec(from, to, end, 1);
}
/* /*
Convert decimal to double Convert decimal to double
...@@ -766,8 +794,8 @@ int decimal_bin_size(int precision, int scale) ...@@ -766,8 +794,8 @@ int decimal_bin_size(int precision, int scale)
SYNOPSIS SYNOPSIS
decimal_round() decimal_round()
dec - decimal to round, from - decimal to round,
also, it's where the result will be stored to - result buffer. from==to is allowed
scale - to what position to round. can be negative! scale - to what position to round. can be negative!
mode - round to nearest even or truncate mode - round to nearest even or truncate
...@@ -779,44 +807,57 @@ int decimal_bin_size(int precision, int scale) ...@@ -779,44 +807,57 @@ int decimal_bin_size(int precision, int scale)
E_DEC_OK/E_DEC_TRUNCATED E_DEC_OK/E_DEC_TRUNCATED
*/ */
int decimal_round(decimal *dec, int scale, dec_round_mode mode) int decimal_round(decimal *from, decimal *to, int scale, dec_round_mode mode)
{ {
int frac0=ROUND_UP(scale), frac1=ROUND_UP(dec->frac), int frac0=ROUND_UP(scale), frac1=ROUND_UP(from->frac),
intg0=ROUND_UP(dec->intg), error=E_DEC_OK, len=dec->len; intg0=ROUND_UP(from->intg), error=E_DEC_OK, len=to->len;
dec1 *buf=dec->buf, x, y, carry=0; dec1 *buf0=from->buf, *buf1=to->buf, x, y, carry=0;
DBUG_ASSERT(intg0+frac1 <= len); if (unlikely(frac0+intg0 > len))
if (frac0 > frac1)
{ {
if (frac0+intg0 > len) frac0=len-intg0;
{ scale=frac0*DIG_PER_DEC1;
frac0=len-intg0; error=E_DEC_TRUNCATED;
scale=frac0*DIG_PER_DEC1;
error=E_DEC_TRUNCATED;
}
buf+=intg0+frac1;
while (frac0-- > frac1)
*buf++=0;
goto done;
} }
if (scale >= dec->frac)
goto done; /* nothing to do */
if (scale+dec->intg <=0) if (scale+from->intg <=0)
{ {
MAKE_ZERO(dec); decimal_make_zero(to);
return E_DEC_OK; return E_DEC_OK;
} }
if (to != from)
{
dec1 *end=buf0+intg0+min(frac1, frac0);
while (buf0 < end)
*buf1++ = *buf0++;
buf0=from->buf;
buf1=to->buf;
to->sign=from->sign;
to->intg=min(from->intg, len*DIG_PER_DEC1);
}
if (frac0 > frac1)
{
buf1+=intg0+frac1;
while (frac0-- > frac1)
*buf1++=0;
goto done;
}
if (scale >= from->frac)
goto done; /* nothing to do */
DBUG_ASSERT(frac0+intg0 > 0); DBUG_ASSERT(frac0+intg0 > 0);
buf+=intg0+frac0-1; buf0+=intg0+frac0-1;
buf1+=intg0+frac0-1;
if (scale == frac0*DIG_PER_DEC1) if (scale == frac0*DIG_PER_DEC1)
{ {
if (mode != TRUNCATE) if (mode != TRUNCATE)
{ {
x=buf[1]/DIG_MASK; x=buf0[1]/DIG_MASK;
if (x > 5 || (x == 5 && *buf & 1)) if (x > 5 || (x == 5 && *buf0 & 1))
(*buf)++; (*buf1)++;
} }
} }
else else
...@@ -824,23 +865,23 @@ int decimal_round(decimal *dec, int scale, dec_round_mode mode) ...@@ -824,23 +865,23 @@ int decimal_round(decimal *dec, int scale, dec_round_mode mode)
int pos=frac0*DIG_PER_DEC1-scale-1; int pos=frac0*DIG_PER_DEC1-scale-1;
if (mode != TRUNCATE) if (mode != TRUNCATE)
{ {
x=*buf / powers10[pos]; x=*buf1 / powers10[pos];
y=x % 10; y=x % 10;
if (y > 5 || (y == 5 && (x/10) & 1)) if (y > 5 || (y == 5 && (x/10) & 1))
x+=10; x+=10;
*buf=x*powers10[pos]-y; *buf1=x*powers10[pos]-y;
} }
else else
*buf=(*buf/powers10[pos+1])*powers10[pos+1]; *buf1=(*buf1/powers10[pos+1])*powers10[pos+1];
} }
if (*buf > DIG_BASE) if (*buf1 > DIG_BASE)
{ {
carry=1; carry=1;
*buf-=DIG_BASE; *buf1-=DIG_BASE;
while (carry && buf >= dec->buf) while (carry && buf1 >= to->buf)
{ {
ADD(*buf, *buf, 0, carry); ADD(*buf1, *buf1, 0, carry);
buf--; buf1--;
} }
if (unlikely(carry)) if (unlikely(carry))
{ {
...@@ -851,17 +892,17 @@ int decimal_round(decimal *dec, int scale, dec_round_mode mode) ...@@ -851,17 +892,17 @@ int decimal_round(decimal *dec, int scale, dec_round_mode mode)
scale=frac0*DIG_PER_DEC1; scale=frac0*DIG_PER_DEC1;
error=E_DEC_TRUNCATED; /* XXX */ error=E_DEC_TRUNCATED; /* XXX */
} }
for (buf=dec->buf+frac0+intg0; buf > dec->buf; buf--) for (buf1=to->buf+frac0+intg0; buf1 > to->buf; buf1--)
{ {
buf[0]=buf[-1]; buf1[0]=buf1[-1];
} }
*buf=1; *buf1=1;
} }
} }
if (scale<0) scale=0; if (scale<0) scale=0;
done: done:
dec->frac=scale; to->frac=scale;
return error; return error;
} }
...@@ -878,6 +919,11 @@ int decimal_round(decimal *dec, int scale, dec_round_mode mode) ...@@ -878,6 +919,11 @@ int decimal_round(decimal *dec, int scale, dec_round_mode mode)
param - extra param to the operation. unused for '+', '-', '*' param - extra param to the operation. unused for '+', '-', '*'
scale increment for '/' scale increment for '/'
NOTE
returned valued may be larger than the actual buffer requred
in the operation, as decimal_result_size, by design, operates on
precision/scale values only and not on the actual decimal number
RETURN VALUE RETURN VALUE
size of to->buf array in dec1 elements. to get size in bytes size of to->buf array in dec1 elements. to get size in bytes
multiply by sizeof(dec1) multiply by sizeof(dec1)
...@@ -890,17 +936,8 @@ int decimal_result_size(decimal *from1, decimal *from2, char op, int param) ...@@ -890,17 +936,8 @@ int decimal_result_size(decimal *from1, decimal *from2, char op, int param)
return ROUND_UP(max(from1->intg, from2->intg)) + return ROUND_UP(max(from1->intg, from2->intg)) +
ROUND_UP(max(from1->frac, from2->frac)); ROUND_UP(max(from1->frac, from2->frac));
case '+': case '+':
{ return ROUND_UP(max(from1->intg, from2->intg)+1) +
int intg1=from1->intg, intg2=from2->intg, intg0=max(intg1, intg2); ROUND_UP(max(from1->frac, from2->frac));
dec1 x;
/* is there a need for extra word because of carry ? */
x=intg1 > intg2 ? from1->buf[0] :
intg2 > intg1 ? from2->buf[0] :
from1->buf[0] + from2->buf[0] ;
if (unlikely(x > DIG_MASK*9)) /* yes, there is */
intg0+=DIG_PER_DEC1;
return ROUND_UP(intg0) + ROUND_UP(max(from1->frac, from2->frac));
}
case '*': case '*':
return ROUND_UP(from1->intg+from2->intg)+ return ROUND_UP(from1->intg+from2->intg)+
ROUND_UP(from1->frac)+ROUND_UP(from2->frac); ROUND_UP(from1->frac)+ROUND_UP(from2->frac);
...@@ -1027,7 +1064,7 @@ static int do_sub(decimal *from1, decimal *from2, decimal *to) ...@@ -1027,7 +1064,7 @@ static int do_sub(decimal *from1, decimal *from2, decimal *to)
{ {
if (to == 0) /* decimal_cmp() */ if (to == 0) /* decimal_cmp() */
return 0; return 0;
MAKE_ZERO(to); decimal_make_zero(to);
return E_DEC_OK; return E_DEC_OK;
} }
} }
...@@ -1238,7 +1275,7 @@ static int do_div_mod(decimal *from1, decimal *from2, ...@@ -1238,7 +1275,7 @@ static int do_div_mod(decimal *from1, decimal *from2,
} }
if (prec1 <= 0) if (prec1 <= 0)
{ /* short-circuit everything: from1 == 0 */ { /* short-circuit everything: from1 == 0 */
MAKE_ZERO(to); decimal_make_zero(to);
return E_DEC_OK; return E_DEC_OK;
} }
for (i=(prec1-1) % DIG_PER_DEC1; *buf1 < powers10[i--]; prec1--) ; for (i=(prec1-1) % DIG_PER_DEC1; *buf1 < powers10[i--]; prec1--) ;
...@@ -1418,7 +1455,7 @@ static int do_div_mod(decimal *from1, decimal *from2, ...@@ -1418,7 +1455,7 @@ static int do_div_mod(decimal *from1, decimal *from2,
{ {
if (unlikely(-intg0 >= to->len)) if (unlikely(-intg0 >= to->len))
{ {
MAKE_ZERO(to); decimal_make_zero(to);
error=E_DEC_TRUNCATED; error=E_DEC_TRUNCATED;
goto done; goto done;
} }
...@@ -1762,9 +1799,9 @@ void test_ro(char *s1, int n, dec_round_mode mode) ...@@ -1762,9 +1799,9 @@ void test_ro(char *s1, int n, dec_round_mode mode)
sprintf(s, "%s('%s', %d)", (mode == TRUNCATE ? "truncate" : "round"), sprintf(s, "%s('%s', %d)", (mode == TRUNCATE ? "truncate" : "round"),
s1, n); s1, n);
string2decimal(s1, &a, 0); string2decimal(s1, &a, 0);
res=decimal_round(&a, n, mode); res=decimal_round(&a, &b, n, mode);
printf("%-40s => res=%d ", s, res); printf("%-40s => res=%d ", s, res);
print_decimal(&a); print_decimal(&b);
printf("\n"); printf("\n");
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment