summaryrefslogtreecommitdiff
path: root/strings
diff options
context:
space:
mode:
authorunknown <serg@serg.mylan>2004-10-19 14:38:54 +0200
committerunknown <serg@serg.mylan>2004-10-19 14:38:54 +0200
commit24b608b23084f0576bd94066c887b4891d55233d (patch)
treea1338958aee4a73f5dce3c56ccc9fbde4793c32f /strings
parentbd5bafe1b67f42d3737437a58fe6e67b05c03e3b (diff)
downloadmariadb-git-24b608b23084f0576bd94066c887b4891d55233d.tar.gz
decimal to/from bin, and utility functions
strings/Makefile.am: cleanup
Diffstat (limited to 'strings')
-rw-r--r--strings/Makefile.am9
-rw-r--r--strings/decimal.c295
2 files changed, 290 insertions, 14 deletions
diff --git a/strings/Makefile.am b/strings/Makefile.am
index c5b5c2fe30b..31b5195d5cb 100644
--- a/strings/Makefile.am
+++ b/strings/Makefile.am
@@ -74,14 +74,13 @@ if ASSEMBLER
endif
FLAGS=$(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) @NOINST_LDFLAGS@
-LIBS=libmystrings.a
-str_test: str_test.c $(LIBRARIES)
- $(LINK) $(FLAGS) -DMAIN $(INCLUDES) $(srcdir)/str_test.c $(LDADD) $(LIBS) $(pkglib_LIBRARIES)
+str_test: str_test.c $(pkglib_LIBRARIES)
+ $(LINK) $(FLAGS) -DMAIN $(INCLUDES) $(srcdir)/str_test.c $(LDADD) $(pkglib_LIBRARIES)
-test_decimal$(EXEEXT): decimal.c $(LIBRARIES)
+test_decimal$(EXEEXT): decimal.c $(pkglib_LIBRARIES)
$(CP) $(srcdir)/decimal.c ./test_decimal.c
- $(LINK) $(FLAGS) -DMAIN ./test_decimal.c $(LDADD) $(LIBS)
+ $(LINK) $(FLAGS) -DMAIN ./test_decimal.c $(LDADD) $(pkglib_LIBRARIES)
$(RM) -f ./test_decimal.c
# Don't update the files from bitkeeper
diff --git a/strings/decimal.c b/strings/decimal.c
index 6583997b39a..afe5c8697d0 100644
--- a/strings/decimal.c
+++ b/strings/decimal.c
@@ -99,6 +99,9 @@
*/
#include <decimal.h>
+#include <m_ctype.h>
+#include <myisampack.h>
+#include <my_sys.h> /* for my_alloca */
typedef decimal_digit dec1;
typedef longlong dec2;
@@ -110,6 +113,7 @@ typedef longlong dec2;
#define ROUND_UP(X) (((X)+DIG_PER_DEC1-1)/DIG_PER_DEC1)
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 FIX_INTG_FRAC_ERROR(len, intg1, frac1, error) \
do \
@@ -186,7 +190,7 @@ static const dec1 powers10[DIG_PER_DEC1+1]={
} while(0)
/*
- Convert decimal to its string representation
+ Convert decimal to its printable string representation
SYNOPSIS
decimal2string()
@@ -199,7 +203,7 @@ static const dec1 powers10[DIG_PER_DEC1+1]={
E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW
*/
-int decimal2string(decimal *from, char *to, uint *to_len)
+int decimal2string(decimal *from, char *to, int *to_len)
{
int len, intg=from->intg, frac=from->frac, i;
int error=E_DEC_OK;
@@ -209,9 +213,11 @@ int decimal2string(decimal *from, char *to, uint *to_len)
DBUG_ASSERT(*to_len > 2+from->sign);
/* removing leading zeroes */
+ i=intg % DIG_PER_DEC1;
while (intg > 0 && *buf0 == 0)
{
- intg-=DIG_PER_DEC1;
+ intg-=i;
+ i=DIG_PER_DEC1;
buf0++;
}
if (intg > 0)
@@ -231,7 +237,7 @@ int decimal2string(decimal *from, char *to, uint *to_len)
len= from->sign + intg + test(frac) + frac;
if (unlikely(len > --*to_len)) /* reserve one byte for \0 */
{
- uint i=len-*to_len;
+ int i=len-*to_len;
error= (frac && i <= frac + 1) ? E_DEC_TRUNCATED : E_DEC_OVERFLOW;
if (frac && i >= frac + 1) i--;
if (i > frac)
@@ -518,6 +524,244 @@ int decimal2longlong(decimal *from, longlong *to)
}
/*
+ Convert decimal to its binary fixed-length representation
+ two representations of the same length can be compared with memcmp
+ with the correct -1/0/+1 result
+
+ SYNOPSIS
+ decimal2bin()
+ from - value to convert
+ to - points to buffer where string representation should be stored
+ precision/scale - see decimal_bin_size() below
+
+ NOTE
+ the buffer is assumed to be of the size decimal_bin_size(precision, scale)
+
+ RETURN VALUE
+ E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW
+*/
+int decimal2bin(decimal *from, char *to, int precision, int frac)
+{
+ dec1 mask=from->sign ? -1 : 0, *buf1=from->buf, *stop1;
+ int error=E_DEC_OK, intg=precision-frac,
+ intg0=intg/DIG_PER_DEC1,
+ frac0=frac/DIG_PER_DEC1,
+ intg0x=intg-intg0*DIG_PER_DEC1,
+ frac0x=frac-frac0*DIG_PER_DEC1,
+ intg1=from->intg/DIG_PER_DEC1,
+ frac1=from->frac/DIG_PER_DEC1,
+ intg1x=from->intg-intg1*DIG_PER_DEC1,
+ frac1x=from->frac-frac1*DIG_PER_DEC1,
+ isize0=intg0*sizeof(dec1)+dig2bytes[intg0x],
+ fsize0=frac0*sizeof(dec1)+dig2bytes[frac0x],
+ isize1=intg1*sizeof(dec1)+dig2bytes[intg1x],
+ fsize1=frac1*sizeof(dec1)+dig2bytes[frac1x];
+ if (isize0 < isize1)
+ {
+ buf1+=intg1-intg0+(intg1x>0)-(intg0x>0);
+ intg1=intg0; intg1x=intg0x;
+ error=E_DEC_OVERFLOW;
+ }
+ else if (isize0 > isize1)
+ {
+ while (isize0-- > isize1)
+ *to++= (char)mask;
+ }
+ if (fsize0 < fsize1)
+ {
+ frac1=frac0; frac1x=frac0x;
+ error=E_DEC_TRUNCATED;
+ }
+ else if (fsize0 > fsize1 && frac1x)
+ {
+ if (frac0 == frac1)
+ frac1x=frac0x;
+ else
+ {
+ frac1++;
+ frac1x=0;
+ }
+ }
+
+ /* intg1x part */
+ if (intg1x)
+ {
+ int i=dig2bytes[intg1x];
+ dec1 x=(*buf1++ % powers10[intg1x]) ^ mask;
+ switch (i)
+ {
+ case 1: mi_int1store(to, x); break;
+ case 2: mi_int2store(to, x); break;
+ case 3: mi_int3store(to, x); break;
+ case 4: mi_int4store(to, x); break;
+ default: DBUG_ASSERT(0);
+ }
+ to+=i;
+ }
+
+ /* intg1+frac1 part */
+ for (stop1=buf1+intg1+frac1; buf1 < stop1; to+=sizeof(dec1))
+ {
+ dec1 x=*buf1++ ^ mask;
+ DBUG_ASSERT(sizeof(dec1) == 4);
+ mi_int4store(to, x);
+ }
+
+ /* frac1x part */
+ if (frac1x)
+ {
+ int i=dig2bytes[frac1x];
+ dec1 x=(*buf1 / powers10[DIG_PER_DEC1 - frac1x]) ^ mask;
+ switch (i)
+ {
+ case 1: mi_int1store(to, x); break;
+ case 2: mi_int2store(to, x); break;
+ case 3: mi_int3store(to, x); break;
+ case 4: mi_int4store(to, x); break;
+ default: DBUG_ASSERT(0);
+ }
+ to+=i;
+ }
+ if (fsize0 > fsize1)
+ {
+ while (fsize0-- > fsize1)
+ *to++=(uchar)mask;
+ }
+ return error;
+}
+
+/*
+ Restores decimal from its binary fixed-length representation
+
+ SYNOPSIS
+ bin2decimal()
+ from - value to convert
+ to - result
+ precision/scale - see decimal_bin_size() below
+
+ NOTE
+ see decimal2bin()
+ the buffer is assumed to be of the size decimal_bin_size(precision, scale)
+
+ RETURN VALUE
+ E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW
+*/
+
+int bin2decimal(char *from, decimal *to, int precision, int scale)
+{
+ int error=E_DEC_OK, intg=precision-scale,
+ intg0=intg/DIG_PER_DEC1, frac0=scale/DIG_PER_DEC1,
+ intg0x=intg-intg0*DIG_PER_DEC1, frac0x=scale-frac0*DIG_PER_DEC1,
+ intg1=intg0+(intg0x>0), frac1=frac0+(frac0x>0);
+ dec1 *buf=to->buf, mask=(*from <0) ? -1 : 0;
+ char *stop;
+
+ FIX_INTG_FRAC_ERROR(to->len, intg1, frac1, error);
+ if (unlikely(error))
+ {
+ if (intg1 < intg0+(intg0x>0))
+ {
+ from+=dig2bytes[intg0x]+sizeof(dec1)*(intg0-intg1);
+ frac0=frac0x=intg0x=0;
+ intg0=intg1;
+ }
+ else
+ {
+ frac0x=0;
+ frac0=frac1;
+ }
+ }
+
+ to->sign=(mask != 0);
+ to->intg=intg0*DIG_PER_DEC1+intg0x;
+ to->frac=frac0*DIG_PER_DEC1+frac0x;
+
+ if (intg0x)
+ {
+ int i=dig2bytes[intg0x];
+ dec1 x;
+ switch (i)
+ {
+ case 1: x=mi_sint1korr(from); break;
+ case 2: x=mi_sint2korr(from); break;
+ case 3: x=mi_sint3korr(from); break;
+ case 4: x=mi_sint4korr(from); break;
+ default: DBUG_ASSERT(0);
+ }
+ from+=i;
+ *buf=x ^ mask;
+ if (buf > to->buf || *buf != 0)
+ buf++;
+ else
+ to->intg-=intg0x;
+ }
+ for (stop=from+intg0*sizeof(dec1); from < stop; from+=sizeof(dec1))
+ {
+ DBUG_ASSERT(sizeof(dec1) == 4);
+ *buf=mi_sint4korr(from) ^ mask;
+ if (buf > to->buf || *buf != 0)
+ buf++;
+ else
+ to->intg-=DIG_PER_DEC1;
+ }
+ DBUG_ASSERT(to->intg >=0);
+ for (stop=from+frac0*sizeof(dec1); from < stop; from+=sizeof(dec1))
+ {
+ DBUG_ASSERT(sizeof(dec1) == 4);
+ *buf=mi_sint4korr(from) ^ mask;
+ buf++;
+ }
+ if (frac0x)
+ {
+ int i=dig2bytes[frac0x];
+ dec1 x;
+ switch (i)
+ {
+ case 1: x=mi_sint1korr(from); break;
+ case 2: x=mi_sint2korr(from); break;
+ case 3: x=mi_sint3korr(from); break;
+ case 4: x=mi_sint4korr(from); break;
+ default: DBUG_ASSERT(0);
+ }
+ *buf=(x ^ mask) * powers10[DIG_PER_DEC1 - frac0x];
+ buf++;
+ }
+ return error;
+}
+
+/*
+ Returns the size of array to hold a decimal with given precision and scale
+
+ RETURN VALUE
+ size in dec1
+ (multiply by sizeof(dec1) to get the size if bytes)
+*/
+
+int decimal_size(int precision, int scale)
+{
+ DBUG_ASSERT(scale >= 0 && precision > 0 && scale <= precision);
+ return ROUND_UP(precision-scale)+ROUND_UP(scale);
+}
+
+/*
+ Returns the size of array to hold a binary representation of a decimal
+
+ RETURN VALUE
+ size in bytes
+*/
+
+int decimal_bin_size(int precision, int scale)
+{
+ int intg=precision-scale,
+ intg0=intg/DIG_PER_DEC1, frac0=scale/DIG_PER_DEC1,
+ intg0x=intg-intg0*DIG_PER_DEC1, frac0x=scale-frac0*DIG_PER_DEC1;
+
+ DBUG_ASSERT(scale >= 0 && precision > 0 && scale <= precision);
+ return intg0*sizeof(dec1)+dig2bytes[intg0x]+
+ frac0*sizeof(dec1)+dig2bytes[frac0x];
+}
+
+/*
Rounds the decimal to "scale" digits
SYNOPSIS
@@ -538,7 +782,7 @@ int decimal2longlong(decimal *from, longlong *to)
int decimal_round(decimal *dec, int scale, dec_round_mode mode)
{
int frac0=ROUND_UP(scale), frac1=ROUND_UP(dec->frac),
- intg0=ROUND_UP(dec->intg), error=E_DEC_OK, pos, len=dec->len;
+ intg0=ROUND_UP(dec->intg), error=E_DEC_OK, len=dec->len;
dec1 *buf=dec->buf, x, y, carry=0;
DBUG_ASSERT(intg0+frac1 <= len);
@@ -659,18 +903,19 @@ int decimal_result_size(decimal *from1, decimal *from2, char op, int param)
}
case '*':
return ROUND_UP(from1->intg+from2->intg)+
- ROUND_UP(from1->frac+from2->frac);
+ ROUND_UP(from1->frac)+ROUND_UP(from2->frac);
case '/':
return ROUND_UP(from1->intg+from2->intg+1+from1->frac+from2->frac+param);
default: DBUG_ASSERT(0);
}
+ return -1; /* shut up the warning */
}
static int do_add(decimal *from1, decimal *from2, decimal *to)
{
int intg1=ROUND_UP(from1->intg), intg2=ROUND_UP(from2->intg),
frac1=ROUND_UP(from1->frac), frac2=ROUND_UP(from2->frac),
- frac0=max(frac1, frac2), intg0=max(intg1, intg2), error, i;
+ frac0=max(frac1, frac2), intg0=max(intg1, intg2), error;
dec1 *buf1, *buf2, *buf0, *stop, *stop2, x, carry;
/* is there a need for extra word because of carry ? */
@@ -743,8 +988,8 @@ static int do_sub(decimal *from1, decimal *from2, decimal *to)
{
int intg1=ROUND_UP(from1->intg), intg2=ROUND_UP(from2->intg),
frac1=ROUND_UP(from1->frac), frac2=ROUND_UP(from2->frac);
- int frac0=max(frac1, frac2), error, i;
- dec1 *buf1, *buf2, *buf0, *stop1, *stop2, *start1, *start2, x, carry=0;
+ int frac0=max(frac1, frac2), error;
+ dec1 *buf1, *buf2, *buf0, *stop1, *stop2, *start1, *start2, carry=0;
to->sign=from1->sign;
@@ -1333,6 +1578,27 @@ void test_d2f(char *s)
printf("%-40s => res=%d %.*g\n", s1, res, a.intg+a.frac, x);
}
+void test_d2b2d(char *str, int p, int s)
+{
+ char s1[100], buf[100];
+ double x;
+ int res, i, size=decimal_bin_size(p, s);
+
+ sprintf(s1, "'%s'", str);
+ string2decimal(str, &a, 0);
+ res=decimal2bin(&a, buf, p, s);
+ printf("%-31s {%2d, %2d} => res=%d size=%-2d ", s1, p, s, res, size);
+ if (full)
+ {
+ printf("0x");
+ for (i=0; i < size; i++)
+ printf("%02x", ((uchar *)buf)[i]);
+ }
+ res=bin2decimal(buf, &a, p, s);
+ printf(" => res=%d ", res);
+ print_decimal(&a);
+ printf("\n");
+}
void test_f2d(double from)
{
int res;
@@ -1622,6 +1888,17 @@ main()
test_md("-234.567","10.555");
test_md("234.567","-10.555");
+ printf("==== decimal2bin/bin2decimal ====\n");
+ test_d2b2d("12345", 5, 0);
+ test_d2b2d("12345", 10, 3);
+ test_d2b2d("123.45", 10, 3);
+ test_d2b2d("-123.45", 20, 10);
+ test_d2b2d(".00012345000098765", 15, 14);
+ test_d2b2d(".00012345000098765", 22, 20);
+ test_d2b2d(".12345000098765", 30, 20);
+ test_d2b2d("-.000000012345000098765", 30, 20);
+ test_d2b2d("1234500009876.5", 30, 5);
+
return 0;
}
#endif