From 669897cb333ad32ab2bb8b8f4ece813279a6145f Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 30 Oct 2004 18:31:35 +0200 Subject: [PATCH 1/6] bugfix and test case --- strings/decimal.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/strings/decimal.c b/strings/decimal.c index c884b333414..5a109bfa3a7 100644 --- a/strings/decimal.c +++ b/strings/decimal.c @@ -206,7 +206,7 @@ int decimal2string(decimal *from, char *to, int *to_len) DBUG_ASSERT(*to_len > 2+from->sign); /* removing leading zeroes */ - i=intg % DIG_PER_DEC1; + i=((intg-1) % DIG_PER_DEC1)+1; while (intg > 0 && *buf0 == 0) { intg-=i; @@ -1874,6 +1874,7 @@ main() printf("==== do_add ====\n"); test_da(".00012345000098765" ,"123.45"); + test_da(".1" ,".45"); test_da("1234500009876.5" ,".00012345000098765"); test_da("9999909999999.5" ,".555"); test_da("99999999" ,"1"); From 2aebffaac75986961ab7f6558694a18f4f2c9a78 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 30 Oct 2004 19:27:54 +0200 Subject: [PATCH 2/6] sanity checks for decimal result buffers --- strings/decimal.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/strings/decimal.c b/strings/decimal.c index 5a109bfa3a7..de6e99eabdd 100644 --- a/strings/decimal.c +++ b/strings/decimal.c @@ -115,6 +115,9 @@ static const dec1 powers10[DIG_PER_DEC1+1]={ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; static const int dig2bytes[DIG_PER_DEC1+1]={0, 1, 1, 2, 2, 3, 3, 3, 4, 4}; +#define sanity(d) DBUG_ASSERT((d)->len >0 && ((d)->buf[0] | \ + (d)->buf[(d)->len-1] | 1)) + #define FIX_INTG_FRAC_ERROR(len, intg1, frac1, error) \ do \ { \ @@ -306,6 +309,8 @@ static int str2dec(char *from, decimal *to, char **end, my_bool fixed) int i, intg, frac, error, intg1, frac1; dec1 x,*buf; + sanity(to); + while (my_isspace(&my_charset_latin1, *s)) s++; if ((to->sign= (*s == '-'))) @@ -461,6 +466,8 @@ static int ull2dec(ulonglong from, decimal *to) ulonglong x=from; dec1 *buf; + sanity(to); + for (intg1=1; from > DIG_BASE; intg1++, from/=DIG_BASE); if (unlikely(intg1 > to->len)) { @@ -684,6 +691,8 @@ int bin2decimal(char *from, decimal *to, int precision, int scale) dec1 *buf=to->buf, mask=(*from <0) ? -1 : 0; char *stop; + sanity(to); + FIX_INTG_FRAC_ERROR(to->len, intg1, frac1, error); if (unlikely(error)) { @@ -813,6 +822,8 @@ int decimal_round(decimal *from, decimal *to, int scale, dec_round_mode mode) intg0=ROUND_UP(from->intg), error=E_DEC_OK, len=to->len; dec1 *buf0=from->buf, *buf1=to->buf, x, y, carry=0; + sanity(to); + if (unlikely(frac0+intg0 > len)) { frac0=len-intg0; @@ -955,6 +966,8 @@ static int do_add(decimal *from1, decimal *from2, decimal *to) frac0=max(frac1, frac2), intg0=max(intg1, intg2), error; dec1 *buf1, *buf2, *buf0, *stop, *stop2, x, carry; + sanity(to); + /* is there a need for extra word because of carry ? */ x=intg1 > intg2 ? from1->buf[0] : intg2 > intg1 ? from2->buf[0] : @@ -1072,6 +1085,8 @@ static int do_sub(decimal *from1, decimal *from2, decimal *to) if (to == 0) /* decimal_cmp() */ return carry == from1->sign ? 1 : -1; + sanity(to); + to->sign=from1->sign; /* ensure that always from1 > from2 (and intg1 >= intg2) */ @@ -1190,6 +1205,8 @@ int decimal_mul(decimal *from1, decimal *from2, decimal *to) dec1 *buf1=from1->buf+intg1, *buf2=from2->buf+intg2, *buf0, *start2, *stop2, *stop1, *start0, carry; + sanity(to); + i=intg0; j=frac0; FIX_INTG_FRAC_ERROR(to->len, intg0, frac0, error); @@ -1265,6 +1282,8 @@ static int do_div_mod(decimal *from1, decimal *from2, if (mod) to=mod; + sanity(to); + /* removing all the leading zeroes */ i=prec1 % DIG_PER_DEC1; while (prec1 > 0 && *buf1 == 0) From b4d0ae29660f72ed5d7301a88e6b240729fac7a2 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 31 Oct 2004 00:02:13 +0200 Subject: [PATCH 3/6] two more places with the same zero-stripping bug --- strings/decimal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/strings/decimal.c b/strings/decimal.c index de6e99eabdd..d523480b073 100644 --- a/strings/decimal.c +++ b/strings/decimal.c @@ -1285,7 +1285,7 @@ static int do_div_mod(decimal *from1, decimal *from2, sanity(to); /* removing all the leading zeroes */ - i=prec1 % DIG_PER_DEC1; + i=((prec1-1) % DIG_PER_DEC1)+1; while (prec1 > 0 && *buf1 == 0) { prec1-=i; @@ -1300,7 +1300,7 @@ static int do_div_mod(decimal *from1, decimal *from2, for (i=(prec1-1) % DIG_PER_DEC1; *buf1 < powers10[i--]; prec1--) ; DBUG_ASSERT(prec1 > 0); - i=prec2 % DIG_PER_DEC1; + i=((prec2-1) % DIG_PER_DEC1)+1; while (prec2 > 0 && *buf2 == 0) { prec2-=i; From bb72d0a06e72c506138cf9eda51a741f2346be09 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 31 Oct 2004 12:29:38 +0100 Subject: [PATCH 4/6] small optimization --- include/decimal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/decimal.h b/include/decimal.h index e1a495f1f2b..5ff0a18f085 100644 --- a/include/decimal.h +++ b/include/decimal.h @@ -76,7 +76,7 @@ int decimal_round(decimal *from, decimal *to, int new_scale, dec_round_mode mode #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) +#define decimal_neg(dec) do { (dec)->sign^=1; } while(0) /* conventions: From 6924450368bf5cd41a8337e54d9234390790fc2b Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 31 Oct 2004 13:15:44 +0100 Subject: [PATCH 5/6] new round mode - half_up --- include/decimal.h | 4 ++-- strings/decimal.c | 31 ++++++++++++++++--------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/include/decimal.h b/include/decimal.h index 5ff0a18f085..2de62af140d 100644 --- a/include/decimal.h +++ b/include/decimal.h @@ -19,7 +19,7 @@ #include -typedef enum {TRUNCATE=0, EVEN} dec_round_mode; +typedef enum {TRUNCATE=0, HALF_EVEN, HALF_UP} decimal_round_mode; typedef int32 decimal_digit; typedef struct st_decimal { @@ -50,7 +50,7 @@ int decimal_cmp(decimal *from1, decimal *from2); int decimal_mul(decimal *from1, decimal *from2, decimal *to); int decimal_div(decimal *from1, decimal *from2, decimal *to, int scale_incr); int decimal_mod(decimal *from1, decimal *from2, decimal *to); -int decimal_round(decimal *from, decimal *to, int new_scale, dec_round_mode mode); +int decimal_round(decimal *from, decimal *to, int new_scale, decimal_round_mode mode); /* the following works only on special "zero" decimal, not on any diff --git a/strings/decimal.c b/strings/decimal.c index d523480b073..3395a814c5e 100644 --- a/strings/decimal.c +++ b/strings/decimal.c @@ -816,7 +816,7 @@ int decimal_bin_size(int precision, int scale) E_DEC_OK/E_DEC_TRUNCATED */ -int decimal_round(decimal *from, decimal *to, int scale, dec_round_mode mode) +int decimal_round(decimal *from, decimal *to, int scale, decimal_round_mode mode) { int frac0=ROUND_UP(scale), frac1=ROUND_UP(from->frac), intg0=ROUND_UP(from->intg), error=E_DEC_OK, len=to->len; @@ -867,7 +867,7 @@ int decimal_round(decimal *from, decimal *to, int scale, dec_round_mode mode) if (mode != TRUNCATE) { x=buf0[1]/DIG_MASK; - if (x > 5 || (x == 5 && *buf0 & 1)) + if (x > 5 || (x == 5 && (mode == HALF_UP || *buf0 & 1))) (*buf1)++; } } @@ -878,7 +878,7 @@ int decimal_round(decimal *from, decimal *to, int scale, dec_round_mode mode) { x=*buf1 / powers10[pos]; y=x % 10; - if (y > 5 || (y == 5 && (x/10) & 1)) + if (y > 5 || (y == 5 && (mode == HALF_UP || (x/10) & 1))) x+=10; *buf1=x*powers10[pos]-y; } @@ -1811,7 +1811,7 @@ void test_md(char *s1, char *s2) printf("\n"); } -void test_ro(char *s1, int n, dec_round_mode mode) +void test_ro(char *s1, int n, decimal_round_mode mode) { char s[100]; int res; @@ -1941,17 +1941,18 @@ main() test_dv("0", "987"); printf("==== decimal_round ====\n"); - test_ro("15.1",0,EVEN); - test_ro("15.5",0,EVEN); - test_ro("15.9",0,EVEN); - test_ro("-15.1",0,EVEN); - test_ro("-15.5",0,EVEN); - test_ro("-15.9",0,EVEN); - test_ro("15.1",1,EVEN); - test_ro("-15.1",1,EVEN); - test_ro("15.17",1,EVEN); - test_ro("15.4",-1,EVEN); - test_ro("-15.4",-1,EVEN); + test_ro("15.1",0,HALF_UP); + test_ro("15.5",0,HALF_UP); + test_ro("15.5",0,HALF_UP); + test_ro("15.9",0,HALF_UP); + test_ro("-15.1",0,HALF_UP); + test_ro("-15.5",0,HALF_UP); + test_ro("-15.9",0,HALF_UP); + test_ro("15.1",1,HALF_UP); + test_ro("-15.1",1,HALF_UP); + test_ro("15.17",1,HALF_UP); + test_ro("15.4",-1,HALF_UP); + test_ro("-15.4",-1,HALF_UP); test_ro("5678.123451",-4,TRUNCATE); test_ro("5678.123451",-3,TRUNCATE); test_ro("5678.123451",-2,TRUNCATE); From a2e7a90bbb64cba7b92ea71c7d3691e52c222d36 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 31 Oct 2004 21:30:19 +0100 Subject: [PATCH 6/6] compare with DIG_BASE corectly rounding bugs fixed --- strings/decimal.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/strings/decimal.c b/strings/decimal.c index 3395a814c5e..5fa24a4c8a6 100644 --- a/strings/decimal.c +++ b/strings/decimal.c @@ -468,7 +468,7 @@ static int ull2dec(ulonglong from, decimal *to) sanity(to); - for (intg1=1; from > DIG_BASE; intg1++, from/=DIG_BASE); + for (intg1=1; from >= DIG_BASE; intg1++, from/=DIG_BASE); if (unlikely(intg1 > to->len)) { intg1=to->len; @@ -880,20 +880,17 @@ int decimal_round(decimal *from, decimal *to, int scale, decimal_round_mode mode y=x % 10; if (y > 5 || (y == 5 && (mode == HALF_UP || (x/10) & 1))) x+=10; - *buf1=x*powers10[pos]-y; + *buf1=powers10[pos]*(x-y); } else *buf1=(*buf1/powers10[pos+1])*powers10[pos+1]; } - if (*buf1 > DIG_BASE) + if (*buf1 >= DIG_BASE) { carry=1; *buf1-=DIG_BASE; - while (carry && buf1 >= to->buf) - { + while (carry && --buf1 >= to->buf) ADD(*buf1, *buf1, 0, carry); - buf1--; - } if (unlikely(carry)) { /* shifting the number to create space for new digit */ @@ -1407,7 +1404,7 @@ static int do_div_mod(decimal *from1, decimal *from2, dlen1=len2; } guess=(norm_factor*x+norm_factor*y/DIG_BASE)/norm2; - if (unlikely(guess > DIG_BASE)) + if (unlikely(guess >= DIG_BASE)) guess=DIG_BASE-1; if (likely(len2>1)) {