mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
decimal_div bug#9501
This commit is contained in:
@ -839,3 +839,6 @@ Error 1365 Division by 0
|
|||||||
INSERT INTO Sow6_2f VALUES ('a59b');
|
INSERT INTO Sow6_2f VALUES ('a59b');
|
||||||
ERROR HY000: Incorrect decimal value: 'a59b' for column 'col1' at row 1
|
ERROR HY000: Incorrect decimal value: 'a59b' for column 'col1' at row 1
|
||||||
drop table Sow6_2f;
|
drop table Sow6_2f;
|
||||||
|
select 10.3330000000000/12.34500000;
|
||||||
|
10.3330000000000/12.34500000
|
||||||
|
0.8370190360469825840421223160000
|
||||||
|
@ -748,7 +748,7 @@ select 1 / 0;
|
|||||||
#+-------+
|
#+-------+
|
||||||
#| 1 / 0 |
|
#| 1 / 0 |
|
||||||
#+-------+
|
#+-------+
|
||||||
#| NULL |
|
#| NULL |
|
||||||
#+-------+
|
#+-------+
|
||||||
#1 row in set, 1 warning (0.00 sec)
|
#1 row in set, 1 warning (0.00 sec)
|
||||||
#
|
#
|
||||||
@ -864,3 +864,8 @@ SELECT MOD(col1,0) FROM Sow6_2f;
|
|||||||
INSERT INTO Sow6_2f VALUES ('a59b');
|
INSERT INTO Sow6_2f VALUES ('a59b');
|
||||||
#-- should return SQLSTATE 22018 invalid character value for cast
|
#-- should return SQLSTATE 22018 invalid character value for cast
|
||||||
drop table Sow6_2f;
|
drop table Sow6_2f;
|
||||||
|
|
||||||
|
#
|
||||||
|
# bug#9501
|
||||||
|
#
|
||||||
|
select 10.3330000000000/12.34500000;
|
||||||
|
@ -1933,16 +1933,16 @@ int decimal_mul(decimal_t *from1, decimal_t *from2, decimal_t *to)
|
|||||||
XXX if this library is to be used with huge numbers of thousands of
|
XXX if this library is to be used with huge numbers of thousands of
|
||||||
digits, fast division must be implemented and alloca should be
|
digits, fast division must be implemented and alloca should be
|
||||||
changed to malloc (or at least fallback to malloc if alloca() fails)
|
changed to malloc (or at least fallback to malloc if alloca() fails)
|
||||||
but then, decimal_mod() should be rewritten too :(
|
but then, decimal_mul() should be rewritten too :(
|
||||||
*/
|
*/
|
||||||
static int do_div_mod(decimal_t *from1, decimal_t *from2,
|
static int do_div_mod(decimal_t *from1, decimal_t *from2,
|
||||||
decimal_t *to, decimal_t *mod, int scale_incr)
|
decimal_t *to, decimal_t *mod, int scale_incr)
|
||||||
{
|
{
|
||||||
int frac1=ROUND_UP(from1->frac)*DIG_PER_DEC1, prec1=from1->intg+frac1,
|
int frac1=ROUND_UP(from1->frac)*DIG_PER_DEC1, prec1=from1->intg+frac1,
|
||||||
frac2=ROUND_UP(from2->frac)*DIG_PER_DEC1, prec2=from2->intg+frac2,
|
frac2=ROUND_UP(from2->frac)*DIG_PER_DEC1, prec2=from2->intg+frac2,
|
||||||
error, i, intg0, frac0, len1, len2, dlen1, dintg, div=(!mod);
|
error, i, intg0, frac0, len1, len2, dintg, div=(!mod);
|
||||||
dec1 *buf0, *buf1=from1->buf, *buf2=from2->buf, *tmp1,
|
dec1 *buf0, *buf1=from1->buf, *buf2=from2->buf, *tmp1,
|
||||||
*start2, *stop2, *stop1, *stop0, norm2, carry, *start1;
|
*start2, *stop2, *stop1, *stop0, norm2, carry, *start1, dcarry;
|
||||||
dec2 norm_factor, x, guess, y;
|
dec2 norm_factor, x, guess, y;
|
||||||
|
|
||||||
LINT_INIT(error);
|
LINT_INIT(error);
|
||||||
@ -2043,7 +2043,7 @@ static int do_div_mod(decimal_t *from1, decimal_t *from2,
|
|||||||
/* removing end zeroes */
|
/* removing end zeroes */
|
||||||
while (*stop2 == 0 && stop2 >= start2)
|
while (*stop2 == 0 && stop2 >= start2)
|
||||||
stop2--;
|
stop2--;
|
||||||
len2= ++stop2 - start2;
|
len2= stop2++ - start2;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
calculating norm2 (normalized *start2) - we need *start2 to be large
|
calculating norm2 (normalized *start2) - we need *start2 to be large
|
||||||
@ -2055,87 +2055,70 @@ static int do_div_mod(decimal_t *from1, decimal_t *from2,
|
|||||||
*/
|
*/
|
||||||
norm_factor=DIG_BASE/(*start2+1);
|
norm_factor=DIG_BASE/(*start2+1);
|
||||||
norm2=(dec1)(norm_factor*start2[0]);
|
norm2=(dec1)(norm_factor*start2[0]);
|
||||||
if (likely(len2>1))
|
if (likely(len2>0))
|
||||||
norm2+=(dec1)(norm_factor*start2[1]/DIG_BASE);
|
norm2+=(dec1)(norm_factor*start2[1]/DIG_BASE);
|
||||||
|
|
||||||
|
if (*start1 < *start2)
|
||||||
|
dcarry=*start1++;
|
||||||
|
else
|
||||||
|
dcarry=0;
|
||||||
|
|
||||||
/* main loop */
|
/* main loop */
|
||||||
for ( ; buf0 < stop0; buf0++)
|
for (; buf0 < stop0; buf0++)
|
||||||
{
|
{
|
||||||
/* short-circuit, if possible */
|
/* short-circuit, if possible */
|
||||||
if (unlikely(*start1 == 0))
|
if (unlikely(dcarry == 0 && *start1 < *start2))
|
||||||
{
|
guess=0;
|
||||||
start1++;
|
|
||||||
if (likely(div))
|
|
||||||
*buf0=0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* D3: make a guess */
|
|
||||||
if (*start1 >= *start2)
|
|
||||||
{
|
|
||||||
x=start1[0];
|
|
||||||
y=start1[1];
|
|
||||||
dlen1=len2-1;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
x=((dec2)start1[0])*DIG_BASE+start1[1];
|
/* D3: make a guess */
|
||||||
y=start1[2];
|
x=start1[0]+((dec2)dcarry)*DIG_BASE;
|
||||||
dlen1=len2;
|
y=start1[1];
|
||||||
}
|
guess=(norm_factor*x+norm_factor*y/DIG_BASE)/norm2;
|
||||||
guess=(norm_factor*x+norm_factor*y/DIG_BASE)/norm2;
|
if (unlikely(guess >= DIG_BASE))
|
||||||
if (unlikely(guess >= DIG_BASE))
|
guess=DIG_BASE-1;
|
||||||
guess=DIG_BASE-1;
|
if (likely(len2>0))
|
||||||
if (likely(len2>1))
|
{
|
||||||
{
|
/* hmm, this is a suspicious trick - I removed normalization here */
|
||||||
/* hmm, this is a suspicious trick - I removed normalization here */
|
if (start2[1]*guess > (x-guess*start2[0])*DIG_BASE+y)
|
||||||
if (start2[1]*guess > (x-guess*start2[0])*DIG_BASE+y)
|
guess--;
|
||||||
guess--;
|
if (unlikely(start2[1]*guess > (x-guess*start2[0])*DIG_BASE+y))
|
||||||
if (unlikely(start2[1]*guess > (x-guess*start2[0])*DIG_BASE+y))
|
guess--;
|
||||||
guess--;
|
DBUG_ASSERT(start2[1]*guess <= (x-guess*start2[0])*DIG_BASE+y);
|
||||||
DBUG_ASSERT(start2[1]*guess <= (x-guess*start2[0])*DIG_BASE+y);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* D4: multiply and subtract */
|
/* D4: multiply and subtract */
|
||||||
buf2=stop2;
|
|
||||||
buf1=start1+dlen1;
|
|
||||||
DBUG_ASSERT(buf1 < stop1);
|
|
||||||
for (carry=0; buf2 > start2; buf1--)
|
|
||||||
{
|
|
||||||
dec1 hi, lo;
|
|
||||||
x=guess * (*--buf2);
|
|
||||||
hi=(dec1)(x/DIG_BASE);
|
|
||||||
lo=(dec1)(x-((dec2)hi)*DIG_BASE);
|
|
||||||
SUB2(*buf1, *buf1, lo, carry);
|
|
||||||
carry+=hi;
|
|
||||||
}
|
|
||||||
for (; buf1 >= start1; buf1--)
|
|
||||||
{
|
|
||||||
SUB2(*buf1, *buf1, 0, carry);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* D5: check the remainder */
|
|
||||||
if (unlikely(carry))
|
|
||||||
{
|
|
||||||
DBUG_ASSERT(carry==1);
|
|
||||||
/* D6: correct the guess */
|
|
||||||
guess--;
|
|
||||||
buf2=stop2;
|
buf2=stop2;
|
||||||
buf1=start1+dlen1;
|
buf1=start1+len2;
|
||||||
|
DBUG_ASSERT(buf1 < stop1);
|
||||||
for (carry=0; buf2 > start2; buf1--)
|
for (carry=0; buf2 > start2; buf1--)
|
||||||
{
|
{
|
||||||
ADD(*buf1, *buf1, *--buf2, carry);
|
dec1 hi, lo;
|
||||||
|
x=guess * (*--buf2);
|
||||||
|
hi=(dec1)(x/DIG_BASE);
|
||||||
|
lo=(dec1)(x-((dec2)hi)*DIG_BASE);
|
||||||
|
SUB2(*buf1, *buf1, lo, carry);
|
||||||
|
carry+=hi;
|
||||||
}
|
}
|
||||||
for (; buf1 >= start1; buf1--)
|
carry= dcarry < carry;
|
||||||
|
|
||||||
|
/* D5: check the remainder */
|
||||||
|
if (unlikely(carry))
|
||||||
{
|
{
|
||||||
SUB2(*buf1, *buf1, 0, carry);
|
/* D6: correct the guess */
|
||||||
|
guess--;
|
||||||
|
buf2=stop2;
|
||||||
|
buf1=start1+len2;
|
||||||
|
for (carry=0; buf2 > start2; buf1--)
|
||||||
|
{
|
||||||
|
ADD(*buf1, *buf1, *--buf2, carry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DBUG_ASSERT(carry==1);
|
|
||||||
}
|
}
|
||||||
if (likely(div))
|
if (likely(div))
|
||||||
*buf0=(dec1)guess;
|
*buf0=(dec1)guess;
|
||||||
if (*start1 == 0)
|
dcarry= *start1;
|
||||||
start1++;
|
start1++;
|
||||||
}
|
}
|
||||||
if (mod)
|
if (mod)
|
||||||
{
|
{
|
||||||
@ -2144,6 +2127,8 @@ static int do_div_mod(decimal_t *from1, decimal_t *from2,
|
|||||||
intg=prec1-frac1
|
intg=prec1-frac1
|
||||||
frac=max(frac1, frac2)=to->frac
|
frac=max(frac1, frac2)=to->frac
|
||||||
*/
|
*/
|
||||||
|
if (dcarry)
|
||||||
|
*--start1=dcarry;
|
||||||
buf0=to->buf;
|
buf0=to->buf;
|
||||||
intg0=ROUND_UP(prec1-frac1)-(start1-tmp1);
|
intg0=ROUND_UP(prec1-frac1)-(start1-tmp1);
|
||||||
frac0=ROUND_UP(to->frac);
|
frac0=ROUND_UP(to->frac);
|
||||||
@ -2753,6 +2738,8 @@ int main()
|
|||||||
test_dv("1.000000000000", "3","0.333333333333333333", 0);
|
test_dv("1.000000000000", "3","0.333333333333333333", 0);
|
||||||
test_dv("1", "1","1.000000000", 0);
|
test_dv("1", "1","1.000000000", 0);
|
||||||
test_dv("0.0123456789012345678912345", "9999999999","0.000000000001234567890246913578148141", 0);
|
test_dv("0.0123456789012345678912345", "9999999999","0.000000000001234567890246913578148141", 0);
|
||||||
|
test_dv("10.333000000", "12.34500","0.837019036046982584042122316", 0);
|
||||||
|
test_dv("10.000000000060", "2","5.000000000030000000", 0);
|
||||||
|
|
||||||
printf("==== decimal_mod ====\n");
|
printf("==== decimal_mod ====\n");
|
||||||
test_md("234","10","4", 0);
|
test_md("234","10","4", 0);
|
||||||
|
Reference in New Issue
Block a user