summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore1
-rw-r--r--include/decimal.h68
-rw-r--r--strings/Makefile.am25
-rw-r--r--strings/decimal.c1627
-rw-r--r--strings/llstr.c2
5 files changed, 1714 insertions, 9 deletions
diff --git a/.bzrignore b/.bzrignore
index 1feab85d33d..04b3bb22017 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -920,3 +920,4 @@ vio/test-ssl
vio/test-sslclient
vio/test-sslserver
vio/viotest-ssl
+strings/test_decimal
diff --git a/include/decimal.h b/include/decimal.h
new file mode 100644
index 00000000000..4ac3412c4d5
--- /dev/null
+++ b/include/decimal.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _decimal_h
+#define _decimal_h
+
+#include <my_global.h>
+#include <m_ctype.h>
+#include <my_sys.h> /* for my_alloca */
+
+typedef enum {TRUNCATE=0, EVEN} dec_round_mode;
+typedef uint32 decimal_digit;
+
+typedef struct st_decimal {
+ int intg, frac, len;
+ my_bool sign;
+ decimal_digit *buf;
+} decimal;
+
+int decimal2string(decimal *from, char *to, uint *to_len);
+int string2decimal(char *from, decimal *to, char **end);
+int decimal2ulonglong(decimal *from, ulonglong *to);
+int ulonglong2decimal(ulonglong from, decimal *to);
+int decimal2longlong(decimal *from, longlong *to);
+int longlong2decimal(longlong from, decimal *to);
+int decimal2double(decimal *from, double *to);
+int double2decimal(double from, decimal *to);
+
+int decimal_add(decimal *from1, decimal *from2, decimal *to);
+int decimal_sub(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_mod(decimal *from1, decimal *from2, decimal *to);
+int decimal_result_size(decimal *from1, decimal *from2, char op, int param);
+int decimal_round(decimal *dec, int new_scale, dec_round_mode mode);
+
+/*
+ conventions:
+
+ decimal_smth() == 0 -- everything's ok
+ decimal_smth() <= 0 -- result is usable, precision loss is possible
+ decimal_smth() <= 1 -- result is unusable, but most significant digits
+ can be lost
+ decimal_smth() > 1 -- no result was generated
+*/
+
+#define E_DEC_TRUNCATED -1
+#define E_DEC_OK 0
+#define E_DEC_OVERFLOW 1
+#define E_DEC_DIV_ZERO 2
+#define E_DEC_BAD_NUM 3
+#define E_DEC_OOM 4
+
+#endif
+
diff --git a/strings/Makefile.am b/strings/Makefile.am
index be51e48ef8d..c5b5c2fe30b 100644
--- a/strings/Makefile.am
+++ b/strings/Makefile.am
@@ -22,19 +22,19 @@ pkglib_LIBRARIES = libmystrings.a
# Exact one of ASSEMBLER_X
if ASSEMBLER_x86
ASRCS = strings-x86.s longlong2str-x86.s my_strtoll10-x86.s
-CSRCS = bfill.c bmove.c bmove512.c bchange.c strxnmov.c int2str.c str2int.c r_strinstr.c strtod.c bcmp.c strtol.c strtoul.c strtoll.c strtoull.c llstr.c strnlen.c ctype.c ctype-simple.c ctype-mb.c ctype-big5.c ctype-czech.c ctype-euc_kr.c ctype-gb2312.c ctype-gbk.c ctype-sjis.c ctype-tis620.c ctype-ujis.c ctype-utf8.c ctype-ucs2.c ctype-uca.c ctype-win1250ch.c ctype-bin.c ctype-latin1.c my_vsnprintf.c xml.c ctype-extra.c
+CSRCS = bfill.c bmove.c bmove512.c bchange.c strxnmov.c int2str.c str2int.c r_strinstr.c strtod.c bcmp.c strtol.c strtoul.c strtoll.c strtoull.c llstr.c strnlen.c ctype.c ctype-simple.c ctype-mb.c ctype-big5.c ctype-czech.c ctype-euc_kr.c ctype-gb2312.c ctype-gbk.c ctype-sjis.c ctype-tis620.c ctype-ujis.c ctype-utf8.c ctype-ucs2.c ctype-uca.c ctype-win1250ch.c ctype-bin.c ctype-latin1.c my_vsnprintf.c xml.c decimal.c ctype-extra.c
else
if ASSEMBLER_sparc32
# These file MUST all be on the same line!! Otherwise automake
# generats a very broken makefile
ASRCS = bmove_upp-sparc.s strappend-sparc.s strend-sparc.s strinstr-sparc.s strmake-sparc.s strmov-sparc.s strnmov-sparc.s strstr-sparc.s
-CSRCS = strcont.c strfill.c strcend.c is_prefix.c longlong2str.c bfill.c bmove.c bmove512.c bchange.c strxnmov.c int2str.c str2int.c r_strinstr.c strtod.c bcmp.c strtol.c strtoul.c strtoll.c strtoull.c llstr.c strnlen.c strxmov.c ctype.c ctype-simple.c ctype-mb.c ctype-big5.c ctype-czech.c ctype-euc_kr.c ctype-gb2312.c ctype-gbk.c ctype-sjis.c ctype-tis620.c ctype-ujis.c ctype-utf8.c ctype-ucs2.c ctype-uca.c ctype-win1250ch.c ctype-bin.c ctype-latin1.c my_vsnprintf.c xml.c ctype-extra.c my_strtoll10.c
+CSRCS = strcont.c strfill.c strcend.c is_prefix.c longlong2str.c bfill.c bmove.c bmove512.c bchange.c strxnmov.c int2str.c str2int.c r_strinstr.c strtod.c bcmp.c strtol.c strtoul.c strtoll.c strtoull.c llstr.c strnlen.c strxmov.c ctype.c ctype-simple.c ctype-mb.c ctype-big5.c ctype-czech.c ctype-euc_kr.c ctype-gb2312.c ctype-gbk.c ctype-sjis.c ctype-tis620.c ctype-ujis.c ctype-utf8.c ctype-ucs2.c ctype-uca.c ctype-win1250ch.c ctype-bin.c ctype-latin1.c my_vsnprintf.c xml.c decimal.c ctype-extra.c my_strtoll10.c
else
#no assembler
ASRCS =
# These file MUST all be on the same line!! Otherwise automake
# generats a very broken makefile
-CSRCS = strxmov.c bmove_upp.c strappend.c strcont.c strend.c strfill.c strcend.c is_prefix.c strstr.c strinstr.c strmake.c strnmov.c strmov.c longlong2str.c bfill.c bmove.c bmove512.c bchange.c strxnmov.c int2str.c str2int.c r_strinstr.c strtod.c bcmp.c strtol.c strtoul.c strtoll.c strtoull.c llstr.c strnlen.c ctype.c ctype-simple.c ctype-mb.c ctype-big5.c ctype-czech.c ctype-euc_kr.c ctype-gb2312.c ctype-gbk.c ctype-sjis.c ctype-tis620.c ctype-ujis.c ctype-utf8.c ctype-ucs2.c ctype-uca.c ctype-win1250ch.c ctype-bin.c ctype-latin1.c my_vsnprintf.c xml.c ctype-extra.c my_strtoll10.c
+CSRCS = strxmov.c bmove_upp.c strappend.c strcont.c strend.c strfill.c strcend.c is_prefix.c strstr.c strinstr.c strmake.c strnmov.c strmov.c longlong2str.c bfill.c bmove.c bmove512.c bchange.c strxnmov.c int2str.c str2int.c r_strinstr.c strtod.c bcmp.c strtol.c strtoul.c strtoll.c strtoull.c llstr.c strnlen.c ctype.c ctype-simple.c ctype-mb.c ctype-big5.c ctype-czech.c ctype-euc_kr.c ctype-gb2312.c ctype-gbk.c ctype-sjis.c ctype-tis620.c ctype-ujis.c ctype-utf8.c ctype-ucs2.c ctype-uca.c ctype-win1250ch.c ctype-bin.c ctype-latin1.c my_vsnprintf.c xml.c decimal.c ctype-extra.c my_strtoll10.c
endif
endif
@@ -43,9 +43,9 @@ noinst_PROGRAMS = conf_to_src
DISTCLEANFILES = ctype_autoconf.c
# Default charset definitions
EXTRA_DIST = ctype-big5.c ctype-czech.c ctype-euc_kr.c ctype-win1250ch.c \
- ctype-gb2312.c ctype-gbk.c ctype-sjis.c ctype-utf8.c \
- ctype-ucs2.c ctype-uca.c ctype-tis620.c ctype-ujis.c \
- xml.c strto.c strings-x86.s \
+ ctype-gb2312.c ctype-gbk.c ctype-sjis.c ctype-utf8.c \
+ ctype-ucs2.c ctype-uca.c ctype-tis620.c ctype-ujis.c \
+ xml.c decimal.c strto.c strings-x86.s \
longlong2str.c longlong2str-x86.s \
my_strtoll10.c my_strtoll10-x86.s \
strxmov.c bmove_upp.c strappend.c strcont.c strend.c \
@@ -54,7 +54,7 @@ EXTRA_DIST = ctype-big5.c ctype-czech.c ctype-euc_kr.c ctype-win1250ch.c \
bmove_upp-sparc.s strappend-sparc.s strend-sparc.s \
strinstr-sparc.s strmake-sparc.s strmov-sparc.s \
strnmov-sparc.s strstr-sparc.s strxmov-sparc.s \
- t_ctype.h
+ t_ctype.h
libmystrings_a_LIBADD=
conf_to_src_SOURCES = conf_to_src.c xml.c ctype.c
@@ -73,8 +73,17 @@ if ASSEMBLER
$(AS) $(ASFLAGS) -o $@ $<
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)
+ $(LINK) $(FLAGS) -DMAIN $(INCLUDES) $(srcdir)/str_test.c $(LDADD) $(LIBS) $(pkglib_LIBRARIES)
+
+test_decimal$(EXEEXT): decimal.c $(LIBRARIES)
+ $(CP) $(srcdir)/decimal.c ./test_decimal.c
+ $(LINK) $(FLAGS) -DMAIN ./test_decimal.c $(LDADD) $(LIBS)
+ $(RM) -f ./test_decimal.c
# Don't update the files from bitkeeper
%::SCCS/s.%
+
diff --git a/strings/decimal.c b/strings/decimal.c
new file mode 100644
index 00000000000..6583997b39a
--- /dev/null
+++ b/strings/decimal.c
@@ -0,0 +1,1627 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+=======================================================================
+ NOTE: this library implements SQL standard "exact numeric" type
+ and is not at all generic, but rather intentinally crippled to
+ follow the standard :)
+=======================================================================
+ Quoting the standard
+ (SQL:2003, Part 2 Foundations, aka ISO/IEC 9075-2:2003)
+
+4.4.2 Characteristics of numbers, page 27:
+
+ An exact numeric type has a precision P and a scale S. P is a positive
+ integer that determines the number of significant digits in a
+ particular radix R, where R is either 2 or 10. S is a non-negative
+ integer. Every value of an exact numeric type of scale S is of the
+ form n*10^{-S}, where n is an integer such that ­-R^P <= n <= R^P.
+
+ [...]
+
+ If an assignment of some number would result in a loss of its most
+ significant digit, an exception condition is raised. If least
+ significant digits are lost, implementation-defined rounding or
+ truncating occurs, with no exception condition being raised.
+
+ [...]
+
+ Whenever an exact or approximate numeric value is assigned to an exact
+ numeric value site, an approximation of its value that preserves
+ leading significant digits after rounding or truncating is represented
+ in the declared type of the target. The value is converted to have the
+ precision and scale of the target. The choice of whether to truncate
+ or round is implementation-defined.
+
+ [...]
+
+ All numeric values between the smallest and the largest value,
+ inclusive, in a given exact numeric type have an approximation
+ obtained by rounding or truncation for that type; it is
+ implementation-defined which other numeric values have such
+ approximations.
+
+5.3 <literal>, page 143
+
+ <exact numeric literal> ::=
+ <unsigned integer> [ <period> [ <unsigned integer> ] ]
+ | <period> <unsigned integer>
+
+6.1 <data type>, page 165:
+
+ 19) The <scale> of an <exact numeric type> shall not be greater than
+ the <precision> of the <exact numeric type>.
+
+ 20) For the <exact numeric type>s DECIMAL and NUMERIC:
+
+ a) The maximum value of <precision> is implementation-defined.
+ <precision> shall not be greater than this value.
+ b) The maximum value of <scale> is implementation-defined. <scale>
+ shall not be greater than this maximum value.
+
+ 21) NUMERIC specifies the data type exact numeric, with the decimal
+ precision and scale specified by the <precision> and <scale>.
+
+ 22) DECIMAL specifies the data type exact numeric, with the decimal
+ scale specified by the <scale> and the implementation-defined
+ decimal precision equal to or greater than the value of the
+ specified <precision>.
+
+6.26 <numeric value expression>, page 241:
+
+ 1) If the declared type of both operands of a dyadic arithmetic
+ operator is exact numeric, then the declared type of the result is
+ an implementation-defined exact numeric type, with precision and
+ scale determined as follows:
+
+ a) Let S1 and S2 be the scale of the first and second operands
+ respectively.
+ b) The precision of the result of addition and subtraction is
+ implementation-defined, and the scale is the maximum of S1 and S2.
+ c) The precision of the result of multiplication is
+ implementation-defined, and the scale is S1 + S2.
+ d) The precision and scale of the result of division are
+ implementation-defined.
+*/
+
+#include <decimal.h>
+
+typedef decimal_digit dec1;
+typedef longlong dec2;
+
+#define DIG_PER_DEC1 9
+#define DIG_MASK 100000000
+#define DIG_BASE 1000000000
+#define DIG_BASE2 LL(1000000000000000000)
+#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};
+
+#define FIX_INTG_FRAC_ERROR(len, intg1, frac1, error) \
+ do \
+ { \
+ if (unlikely(intg1+frac1 > (len))) \
+ { \
+ if (unlikely(intg1 > (len))) \
+ { \
+ intg1=(len); \
+ frac1=0; \
+ error=E_DEC_OVERFLOW; \
+ } \
+ else \
+ { \
+ frac1=(len)-intg1; \
+ error=E_DEC_TRUNCATED; \
+ } \
+ } \
+ else \
+ error=E_DEC_OK; \
+ } while(0)
+
+#define ADD(to, from1, from2, carry) /* assume carry <= 1 */ \
+ do \
+ { \
+ dec1 a=(from1)+(from2)+(carry); \
+ if (((carry)= a >= DIG_BASE)) /* no division here! */ \
+ a-=DIG_BASE; \
+ (to)=a; \
+ } while(0)
+
+#define ADD2(to, from1, from2, carry) \
+ do \
+ { \
+ dec1 a=(from1)+(from2)+(carry); \
+ if (((carry)= a >= DIG_BASE)) \
+ a-=DIG_BASE; \
+ if (unlikely(a >= DIG_BASE)) \
+ { \
+ a-=DIG_BASE; \
+ carry++; \
+ } \
+ (to)=a; \
+ } while(0)
+
+#define SUB(to, from1, from2, carry) /* to=from1-from2 */ \
+ do \
+ { \
+ dec1 a=(from1)-(from2)-(carry); \
+ if (((carry)= a < 0)) \
+ a+=DIG_BASE; \
+ (to)=a; \
+ } while(0)
+
+#define SUB2(to, from1, from2, carry) /* to=from1-from2 */ \
+ do \
+ { \
+ dec1 a=(from1)-(from2)-(carry); \
+ if (((carry)= a < 0)) \
+ a+=DIG_BASE; \
+ if (unlikely(a < 0)) \
+ { \
+ a+=DIG_BASE; \
+ carry++; \
+ } \
+ (to)=a; \
+ } 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 string representation
+
+ SYNOPSIS
+ decimal2string()
+ from - value to convert
+ to - points to buffer where string representation should be stored
+ *to_len - in: size of to buffer
+ out: length of the actually written string
+
+ RETURN VALUE
+ E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW
+*/
+
+int decimal2string(decimal *from, char *to, uint *to_len)
+{
+ int len, intg=from->intg, frac=from->frac, i;
+ int error=E_DEC_OK;
+ char *s=to;
+ dec1 *buf, *buf0=from->buf, tmp;
+
+ DBUG_ASSERT(*to_len > 2+from->sign);
+
+ /* removing leading zeroes */
+ while (intg > 0 && *buf0 == 0)
+ {
+ intg-=DIG_PER_DEC1;
+ buf0++;
+ }
+ if (intg > 0)
+ {
+ for (i=(intg-1) % DIG_PER_DEC1; *buf0 < powers10[i--]; intg--) ;
+ DBUG_ASSERT(intg > 0);
+ }
+ else
+ intg=0;
+ if (unlikely(intg+frac==0))
+ {
+ intg=1;
+ tmp=0;
+ buf0=&tmp;
+ }
+
+ len= from->sign + intg + test(frac) + frac;
+ if (unlikely(len > --*to_len)) /* reserve one byte for \0 */
+ {
+ uint i=len-*to_len;
+ error= (frac && i <= frac + 1) ? E_DEC_TRUNCATED : E_DEC_OVERFLOW;
+ if (frac && i >= frac + 1) i--;
+ if (i > frac)
+ {
+ intg-= i-frac;
+ frac= 0;
+ }
+ else
+ frac-=i;
+ len= from->sign + intg + test(frac) + frac;
+ }
+ *to_len=len;
+ s[len]=0;
+
+ if (from->sign)
+ *s++='-';
+
+ if (frac)
+ {
+ char *s1=s+intg;
+ buf=buf0+ROUND_UP(intg);
+ *s1++='.';
+ for (; frac>0; frac-=DIG_PER_DEC1)
+ {
+ dec1 x=*buf++;
+ for (i=min(frac, DIG_PER_DEC1); i; i--)
+ {
+ dec1 y=x/DIG_MASK;
+ *s1++='0'+(uchar)y;
+ x-=y*DIG_MASK;
+ x*=10;
+ }
+ }
+ }
+
+ s+=intg;
+ for (buf=buf0+ROUND_UP(intg); intg>0; intg-=DIG_PER_DEC1)
+ {
+ dec1 x=*--buf;
+ for (i=min(intg, DIG_PER_DEC1); i; i--)
+ {
+ dec1 y=x/10;
+ *--s='0'+(uchar)(x-y*10);
+ x=y;
+ }
+ }
+ return error;
+}
+
+/*
+ Convert string to decimal
+
+ SYNOPSIS
+ string2decimal()
+ from - value to convert
+ to - decimal where where the result will be stored
+ to->buf and to->len must be set.
+ end - if not NULL, *end will be set to the char where
+ conversion ended
+
+ RETURN VALUE
+ E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW/E_DEC_BAD_NUM;
+*/
+
+int string2decimal(char *from, decimal *to, char **end)
+{
+ char *s=from, *s1;
+ int i, intg, frac, error, intg1, frac1;
+ dec1 x,*buf;
+
+ while (my_isspace(&my_charset_latin1, *s))
+ s++;
+ if ((to->sign= (*s == '-')))
+ s++;
+ else if (*s == '+')
+ s++;
+
+ s1=s;
+ while (my_isdigit(&my_charset_latin1, *s))
+ s++;
+ intg=s-s1;
+ if (*s=='.')
+ {
+ char *s2=s+1;
+ while (my_isdigit(&my_charset_latin1, *s2))
+ s2++;
+ frac=s2-s-1;
+ }
+ else
+ frac=0;
+ if (end)
+ *end=s1+intg+frac+test(frac);
+
+ if (frac+intg == 0)
+ return E_DEC_BAD_NUM;
+
+ intg1=ROUND_UP(intg);
+ frac1=ROUND_UP(frac);
+ 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->frac=frac;
+
+ buf=to->buf+intg1;
+ s1=s;
+
+ for (x=0, i=0; intg; intg--)
+ {
+ x+= (*--s - '0')*powers10[i];
+
+ if (unlikely(++i == DIG_PER_DEC1))
+ {
+ *--buf=x;
+ x=0;
+ i=0;
+ }
+ }
+ if (i)
+ *--buf=x;
+
+ buf=to->buf+intg1;
+ for (x=0, i=0; frac; frac--)
+ {
+ x= (*++s1 - '0') + x*10;
+
+ if (unlikely(++i == DIG_PER_DEC1))
+ {
+ *buf++=x;
+ x=0;
+ i=0;
+ }
+ }
+ if (i)
+ *buf=x*powers10[DIG_PER_DEC1-i];
+
+ return error;
+}
+
+/*
+ Convert decimal to double
+
+ SYNOPSIS
+ decimal2double()
+ from - value to convert
+ to - result will be stored there
+
+ RETURN VALUE
+ E_DEC_OK
+*/
+
+int decimal2double(decimal *from, double *to)
+{
+ double x=0, t=DIG_BASE;
+ int intg, frac;
+ dec1 *buf=from->buf;
+
+ for (intg=from->intg; intg > 0; intg-=DIG_PER_DEC1)
+ x=x*DIG_BASE + *buf++;
+ for (frac=from->frac; frac > 0; frac-=DIG_PER_DEC1, t*=DIG_BASE)
+ x+=*buf++/t;
+ *to=from->sign ? -x : x;
+ return E_DEC_OK;
+}
+
+/*
+ Convert double to decimal
+
+ SYNOPSIS
+ double2decimal()
+ from - value to convert
+ to - result will be stored there
+
+ RETURN VALUE
+ E_DEC_OK/E_DEC_OVERFLOW/E_DEC_TRUNCATED
+*/
+
+int double2decimal(double from, decimal *to)
+{
+ /* TODO: fix it, when we'll have dtoa */
+ char s[400];
+ sprintf(s, "%f", from);
+ return string2decimal(s, to, 0);
+}
+
+static int ull2dec(ulonglong from, decimal *to)
+{
+ int intg1, error=E_DEC_OK;
+ ulonglong x=from;
+ dec1 *buf;
+
+ for (intg1=1; from > DIG_BASE; intg1++, from/=DIG_BASE);
+ if (unlikely(intg1 > to->len))
+ {
+ intg1=to->len;
+ error=E_DEC_OVERFLOW;
+ }
+ to->frac=0;
+ to->intg=intg1*DIG_PER_DEC1;
+
+ for (buf=to->buf+intg1; intg1; intg1--)
+ {
+ ulonglong y=x/DIG_BASE;
+ *--buf=(dec1)(x-y*DIG_BASE);
+ x=y;
+ }
+ return error;
+}
+
+int ulonglong2decimal(ulonglong from, decimal *to)
+{
+ to->sign=0;
+ return ull2dec(from, to);
+}
+
+int longlong2decimal(longlong from, decimal *to)
+{
+ if ((to->sign= from < 0))
+ return ull2dec(-from, to);
+ return ull2dec(from, to);
+}
+
+int decimal2ulonglong(decimal *from, ulonglong *to)
+{
+ dec1 *buf=from->buf;
+ ulonglong x=0;
+ int intg;
+
+ if (from->sign)
+ {
+ *to=ULL(0);
+ return E_DEC_OVERFLOW;
+ }
+
+ for (intg=from->intg; intg > 0; intg-=DIG_PER_DEC1)
+ {
+ ulonglong y=x;
+ x=x*DIG_BASE + *buf++;
+ if (unlikely(x < y))
+ {
+ *to=y;
+ return E_DEC_OVERFLOW;
+ }
+ }
+ *to=x;
+ return from->frac ? E_DEC_TRUNCATED : E_DEC_OK;
+}
+
+int decimal2longlong(decimal *from, longlong *to)
+{
+ dec1 *buf=from->buf;
+ longlong x=0;
+ int intg;
+
+ for (intg=from->intg; intg > 0; intg-=DIG_PER_DEC1)
+ {
+ longlong y=x;
+ /*
+ Attention: trick!
+ we're calculating -|from| instead of |from| here
+ because |MIN_LONGLONG| > MAX_LONGLONG
+ so we can convert -9223372036854775808 correctly
+ */
+ x=x*DIG_BASE - *buf++;
+ if (unlikely(x > y))
+ {
+ *to= from->sign ? y : -y;
+ return E_DEC_OVERFLOW;
+ }
+ }
+ /* boundary case: 9223372036854775808 */
+ if (unlikely(from->sign==0 && x < 0 && -x < 0))
+ {
+ *to= -1-x;
+ return E_DEC_OVERFLOW;
+ }
+
+ *to=from->sign ? x : -x;
+ return from->frac ? E_DEC_TRUNCATED : E_DEC_OK;
+}
+
+/*
+ Rounds the decimal to "scale" digits
+
+ SYNOPSIS
+ decimal_round()
+ dec - decimal to round,
+ also, it's where the result will be stored
+ scale - to what position to round. can be negative!
+ mode - round to nearest even or truncate
+
+ NOTES
+ scale can be negative !
+ one TRUNCATED error (line XXX below) isn't treated very logical :(
+
+ RETURN VALUE
+ E_DEC_OK/E_DEC_TRUNCATED
+*/
+
+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;
+ dec1 *buf=dec->buf, x, y, carry=0;
+
+ DBUG_ASSERT(intg0+frac1 <= len);
+ if (frac0 > frac1)
+ {
+ if (frac0+intg0 > len)
+ {
+ frac0=len-intg0;
+ 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)
+ {
+ MAKE_ZERO(dec);
+ return E_DEC_OK;
+ }
+
+ DBUG_ASSERT(frac0+intg0 > 0);
+ buf+=intg0+frac0-1;
+ if (scale == frac0*DIG_PER_DEC1)
+ {
+ if (mode != TRUNCATE)
+ {
+ x=buf[1]/DIG_MASK;
+ if (x > 5 || (x == 5 && *buf & 1))
+ (*buf)++;
+ }
+ }
+ else
+ {
+ int pos=frac0*DIG_PER_DEC1-scale-1;
+ if (mode != TRUNCATE)
+ {
+ x=*buf / powers10[pos];
+ y=x % 10;
+ if (y > 5 || (y == 5 && (x/10) & 1))
+ x+=10;
+ *buf=x*powers10[pos]-y;
+ }
+ else
+ *buf=(*buf/powers10[pos+1])*powers10[pos+1];
+ }
+ if (*buf > DIG_BASE)
+ {
+ carry=1;
+ *buf-=DIG_BASE;
+ while (carry && buf >= dec->buf)
+ {
+ ADD(*buf, *buf, 0, carry);
+ buf--;
+ }
+ if (unlikely(carry))
+ {
+ /* shifting the number to create space for new digit */
+ if (frac0+intg0 >= len)
+ {
+ frac0--;
+ scale=frac0*DIG_PER_DEC1;
+ error=E_DEC_TRUNCATED; /* XXX */
+ }
+ for (buf=dec->buf+frac0+intg0; buf > dec->buf; buf--)
+ {
+ buf[0]=buf[-1];
+ }
+ *buf=1;
+ }
+ }
+ if (scale<0) scale=0;
+
+done:
+ dec->frac=scale;
+ return error;
+}
+
+/*
+ Returns the size of the result of the operation
+
+ SYNOPSIS
+ decimal_result_size()
+ from1 - operand of the unary operation or first operand of the
+ binary operation
+ from2 - second operand of the binary operation
+ op - operation. one char '+', '-', '*', '/' are allowed
+ others may be added later
+ param - extra param to the operation. unused for '+', '-', '*'
+ scale increment for '/'
+
+ RETURN VALUE
+ size of to->buf array in dec1 elements. to get size in bytes
+ multiply by sizeof(dec1)
+*/
+
+int decimal_result_size(decimal *from1, decimal *from2, char op, int param)
+{
+ switch (op) {
+ case '-':
+ return ROUND_UP(max(from1->intg, from2->intg)) +
+ ROUND_UP(max(from1->frac, from2->frac));
+ case '+':
+ {
+ int intg1=from1->intg, intg2=from2->intg, intg0=max(intg1, intg2);
+ 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 '*':
+ return ROUND_UP(from1->intg+from2->intg)+
+ ROUND_UP(from1->frac+from2->frac);
+ case '/':
+ return ROUND_UP(from1->intg+from2->intg+1+from1->frac+from2->frac+param);
+ default: DBUG_ASSERT(0);
+ }
+}
+
+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;
+ dec1 *buf1, *buf2, *buf0, *stop, *stop2, x, carry;
+
+ /* 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++;
+ to->buf[0]=0; /* safety */
+ }
+
+ FIX_INTG_FRAC_ERROR(to->len, intg0, frac0, error);
+ buf0=to->buf+intg0+frac0;
+
+ to->sign=from1->sign;
+ to->frac=max(from1->frac, from2->frac);
+ to->intg=intg0*DIG_PER_DEC1;
+ if (unlikely(error))
+ {
+ set_if_smaller(to->frac, frac0*DIG_PER_DEC1);
+ set_if_smaller(frac1, frac0);
+ set_if_smaller(frac2, frac0);
+ set_if_smaller(intg1, intg0);
+ set_if_smaller(intg2, intg0);
+ }
+
+ /* part 1 - max(frac) ... min (frac) */
+ if (frac1 > frac2)
+ {
+ buf1=from1->buf+intg1+frac1;
+ stop=from1->buf+intg1+frac2;
+ buf2=from2->buf+intg2+frac2;
+ stop2=from1->buf+(intg1 > intg2 ? intg1-intg2 : 0);
+ }
+ else
+ {
+ buf1=from2->buf+intg2+frac2;
+ stop=from2->buf+intg2+frac1;
+ buf2=from1->buf+intg1+frac1;
+ stop2=from2->buf+(intg2 > intg1 ? intg2-intg1 : 0);
+ }
+ while (buf1 > stop)
+ *--buf0=*--buf1;
+
+ /* part 2 - min(frac) ... min(intg) */
+ carry=0;
+ while (buf1 > stop2)
+ {
+ ADD(*--buf0, *--buf1, *--buf2, carry);
+ }
+
+ /* part 3 - min(intg) ... max(intg) */
+ buf1= intg1 > intg2 ? ((stop=from1->buf)+intg1-intg2) :
+ ((stop=from2->buf)+intg2-intg1) ;
+ while (buf1 > stop)
+ {
+ ADD(*--buf0, *--buf1, 0, carry);
+ }
+
+ if (unlikely(carry))
+ *--buf0=1;
+ DBUG_ASSERT(buf0 == to->buf || buf0 == to->buf+1);
+
+ return error;
+}
+
+/* to=from1-from2 */
+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;
+
+ to->sign=from1->sign;
+
+ /* let carry:=1 if from2 > from1 */
+ start1=buf1=from1->buf; stop1=buf1+intg1;
+ start2=buf2=from2->buf; stop2=buf2+intg2;
+ if (unlikely(*buf1 == 0))
+ {
+ while (buf1 < stop1 && *buf1 == 0)
+ buf1++;
+ start1=buf1;
+ intg1=stop1-buf1;
+ }
+ if (unlikely(*buf2 == 0))
+ {
+ while (buf2 < stop2 && *buf2 == 0)
+ buf2++;
+ start2=buf2;
+ intg2=stop2-buf2;
+ }
+ if (intg2 > intg1)
+ carry=1;
+ else if (intg2 == intg1)
+ {
+ while (buf1 < stop1+frac1 && buf2 < stop2+frac2 && *buf1 == *buf2)
+ buf1++, buf2++;
+ if (buf1 < stop1+frac1)
+ if (buf2 < stop2+frac2)
+ carry= *buf2 > *buf1;
+ else
+ carry= 0;
+ else
+ if (buf2 < stop2+frac2)
+ carry=1;
+ else /* short-circuit everything: from1 == from2 */
+ {
+ MAKE_ZERO(to);
+ return E_DEC_OK;
+ }
+ }
+ /* ensure that always from1 > from2 (and intg1 >= intg2) */
+ if (carry)
+ {
+ swap_variables(decimal *,from1,from1);
+ swap_variables(dec1 *,start1, start2);
+ swap_variables(int,intg1,intg2);
+ swap_variables(int,frac1,frac2);
+ to->sign= 1 - to->sign;
+ }
+
+ FIX_INTG_FRAC_ERROR(to->len, intg1, frac0, error);
+ buf0=to->buf+intg1+frac0;
+
+ to->frac=max(from1->frac, from2->frac);
+ to->intg=intg1*DIG_PER_DEC1;
+ if (unlikely(error))
+ {
+ set_if_smaller(to->frac, frac0*DIG_PER_DEC1);
+ set_if_smaller(frac1, frac0);
+ set_if_smaller(frac2, frac0);
+ set_if_smaller(intg2, intg1);
+ }
+ carry=0;
+
+ /* part 1 - max(frac) ... min (frac) */
+ if (frac1 > frac2)
+ {
+ buf1=start1+intg1+frac1;
+ stop1=start1+intg1+frac2;
+ buf2=start2+intg2+frac2;
+ while (buf1 > stop1)
+ *--buf0=*--buf1;
+ }
+ else
+ {
+ buf1=start1+intg1+frac1;
+ buf2=start2+intg2+frac2;
+ stop2=start2+intg2+frac1;
+ while (buf2 > stop2)
+ {
+ SUB(*--buf0, 0, *--buf2, carry);
+ }
+ }
+
+ /* part 2 - min(frac) ... intg2 */
+ while (buf2 > start2)
+ {
+ SUB(*--buf0, *--buf1, *--buf2, carry);
+ }
+
+ /* part 3 - intg2 ... intg1 */
+ while (carry && buf1 > start1)
+ {
+ SUB(*--buf0, *--buf1, 0, carry);
+ }
+
+ while (buf1 > start1)
+ *--buf0=*--buf1;
+
+ while (buf0 > to->buf)
+ *--buf0=0;
+
+ return error;
+}
+
+int decimal_add(decimal *from1, decimal *from2, decimal *to)
+{
+ if (likely(from1->sign == from2->sign))
+ return do_add(from1, from2, to);
+ return do_sub(from1, from2, to);
+}
+
+int decimal_sub(decimal *from1, decimal *from2, decimal *to)
+{
+ if (likely(from1->sign == from2->sign))
+ return do_sub(from1, from2, to);
+ return do_add(from1, from2, to);
+}
+
+/*
+ multiply two decimals
+
+ SYNOPSIS
+ decimal_mul()
+ from1, from2 - factors
+ to - product
+
+ RETURN VALUE
+ E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW;
+
+ NOTES
+ in this implementation, with sizeof(dec1)=4 we have DIG_PER_DEC1=9,
+ and 63-digit number will take only 7 dec1 words (basically a 7-digit
+ "base 999999999" number). Thus there's no need in fast multiplication
+ algorithms, 7-digit numbers can be multiplied with a naive O(n*n)
+ method.
+
+ XXX if this library is to be used with huge numbers of thousands of
+ digits, fast multiplication must be implemented.
+*/
+int decimal_mul(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),
+ intg0=ROUND_UP(from1->intg+from2->intg),
+ frac0=frac1+frac2, error, i, j;
+ dec1 *buf1=from1->buf+intg1, *buf2=from2->buf+intg2, *buf0,
+ *start2, *stop2, *stop1, *start0, carry;
+
+ i=intg0;
+ j=frac0;
+ FIX_INTG_FRAC_ERROR(to->len, intg0, frac0, error);
+ to->sign=from1->sign != from2->sign;
+ to->frac=from1->frac+from2->frac;
+ to->intg=intg0*DIG_PER_DEC1;
+
+ if (unlikely(error))
+ {
+ set_if_smaller(to->frac, frac0*DIG_PER_DEC1);
+ set_if_smaller(to->intg, intg0*DIG_PER_DEC1);
+ if (unlikely(i > intg0))
+ {
+ i-=intg0;
+ j=i >> 1;
+ intg1-= j;
+ intg2-=i-j;
+ frac1=frac2=0; /* frac0 is already 0 here */
+ }
+ else
+ {
+ j-=frac0;
+ i=j >> 1;
+ frac1-= i;
+ frac2-=j-i;
+ }
+ }
+ start0=to->buf+intg0+frac0-1;
+ start2=buf2+frac2-1;
+ stop1=buf1-intg1;
+ stop2=buf2-intg2;
+
+ bzero(to->buf, (intg0+frac0)*sizeof(dec1));
+
+ for (buf1+=frac1-1; buf1 >= stop1; buf1--, start0--)
+ {
+ carry=0;
+ for (buf0=start0, buf2=start2; buf2 >= stop2; buf2--, buf0--)
+ {
+ dec1 hi, lo;
+ dec2 p= ((dec2)*buf1) * ((dec2)*buf2);
+ hi=(dec1)(p/DIG_BASE);
+ lo=(dec1)(p-((dec2)hi)*DIG_BASE);
+ ADD2(*buf0, *buf0, lo, carry);
+ carry+=hi;
+ }
+ for (; carry; buf0--)
+ ADD(*buf0, *buf0, 0, carry);
+ }
+ return error;
+}
+
+/*
+ naive division algorithm (Knuth's Algorithm D in 4.3.1) -
+ it's ok for short numbers
+ also we're using alloca() to allocate a temporary buffer
+
+ XXX if this library is to be used with huge numbers of thousands of
+ digits, fast division must be implemented and alloca should be
+ changed to malloc (or at least fallback to malloc if alloca() fails)
+ but then, decimal_mod() should be rewritten too :(
+*/
+static int do_div_mod(decimal *from1, decimal *from2,
+ decimal *to, decimal *mod, int scale_incr)
+{
+ 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,
+ error, i, intg0, frac0, len1, len2, dlen1;
+ dec1 *buf0, *buf1=from1->buf, *buf2=from2->buf, *tmp1,
+ *start2, *stop2, *stop1, *stop0, norm2, carry, *start1;
+ dec2 norm_factor, x, guess, y;
+
+ if (mod)
+ to=mod;
+
+ /* removing all the leading zeroes */
+ i=prec1 % DIG_PER_DEC1;
+ while (prec1 > 0 && *buf1 == 0)
+ {
+ prec1-=i;
+ i=DIG_PER_DEC1;
+ buf1++;
+ }
+ if (prec1 <= 0)
+ { /* short-circuit everything: from1 == 0 */
+ MAKE_ZERO(to);
+ return E_DEC_OK;
+ }
+ for (i=(prec1-1) % DIG_PER_DEC1; *buf1 < powers10[i--]; prec1--) ;
+ DBUG_ASSERT(prec1 > 0);
+
+ i=prec2 % DIG_PER_DEC1;
+ while (prec2 > 0 && *buf2 == 0)
+ {
+ prec2-=i;
+ i=DIG_PER_DEC1;
+ buf2++;
+ }
+ if (prec2 <= 0) /* short-circuit everything: from2 == 0 */
+ return E_DEC_DIV_ZERO;
+
+ for (i=(prec2-1) % DIG_PER_DEC1; *buf2 < powers10[i--]; prec2--) ;
+ DBUG_ASSERT(prec2 > 0);
+
+ /* let's fix scale_incr, taking into account frac1,frac2 increase */
+ if ((scale_incr-= frac1 - from1->frac + frac2 - from2->frac) < 0)
+ scale_incr=0;
+
+ if ((i=(prec1-frac1)-(prec2-frac2)+1) < 0) /* see below */
+ intg0=0;
+ else
+ intg0=ROUND_UP(i);
+ if (mod)
+ {
+ /* we're calculating N1 % N2.
+ The result will have
+ frac=max(frac1, frac2), as for subtraction
+ intg=intg2
+ */
+ to->sign=from1->sign;
+ to->frac=max(from1->frac, from2->frac);
+ frac0=0;
+ }
+ else
+ {
+ /*
+ we're calculating N1/N2. N1 is in the buf1, has prec1 digits
+ N2 is in the buf2, has prec2 digits. Scales are frac1 and
+ frac2 accordingly.
+ Thus, the result will have
+ frac = ROUND_UP(frac1+frac2+scale_incr)
+ and
+ intg = (prec1-frac1) - (prec2-frac2) + 1
+ prec = intg+frac
+ */
+ frac0=ROUND_UP(frac1+frac2+scale_incr);
+ FIX_INTG_FRAC_ERROR(to->len, intg0, frac0, error);
+ to->sign=from1->sign != from2->sign;
+ to->intg=intg0*DIG_PER_DEC1;
+ to->frac=frac0*DIG_PER_DEC1;
+ }
+ buf0=to->buf;
+ stop0=buf0+intg0+frac0;
+
+ len1=(i=ROUND_UP(prec1))+ROUND_UP(2*frac2+scale_incr+1);
+ if (!(tmp1=my_alloca(len1*sizeof(dec1))))
+ return E_DEC_OOM;
+ memcpy(tmp1, buf1, i*sizeof(dec1));
+ bzero(tmp1+i, (len1-i)*sizeof(dec1));
+
+ start1=tmp1;
+ stop1=start1+len1;
+ start2=buf2;
+ stop2=buf2+ROUND_UP(prec2)-1;
+
+ /* removing end zeroes */
+ while (*stop2 == 0 && stop2 >= start2)
+ stop2--;
+ len2= ++stop2 - start2;
+
+ /*
+ calculating norm2 (normalized *start2) - we need *start2 to be large
+ (at least > DIG_BASE/2), but unlike Knuth's Alg. D we don't want to
+ normalize input numbers (as we don't make a copy of the divisor).
+ Thus we normalize first dec1 of buf2 only, and we'll normalize *start1
+ on the fly for the purpose of guesstimation only.
+ It's also faster, as we're saving on normalization of buf2
+ */
+ norm_factor=DIG_BASE/(*start2+1);
+ norm2=(dec1)(norm_factor*start2[0]);
+ if (likely(len2>1))
+ norm2+=(dec1)(norm_factor*start2[1]/DIG_BASE);
+
+ /* main loop */
+ for ( ; buf0 < stop0; buf0++)
+ {
+ /* short-circuit, if possible */
+ if (unlikely(*start1 == 0))
+ {
+ start1++;
+ *buf0=0;
+ continue;
+ }
+
+ /* D3: make a guess */
+ if (*start1 >= *start2)
+ {
+ x=start1[0];
+ y=start1[1];
+ dlen1=len2-1;
+ }
+ else
+ {
+ x=((dec2)start1[0])*DIG_BASE+start1[1];
+ y=start1[2];
+ dlen1=len2;
+ }
+ guess=(norm_factor*x+norm_factor*y/DIG_BASE)/norm2;
+ if (unlikely(guess > DIG_BASE))
+ guess=DIG_BASE-1;
+ if (likely(len2>1))
+ {
+ /* hmm, this is a suspicious trick - I removed normalization here */
+ if (start2[1]*guess > (x-guess*start2[0])*DIG_BASE+y)
+ guess--;
+ if (unlikely(start2[1]*guess > (x-guess*start2[0])*DIG_BASE+y))
+ guess--;
+ DBUG_ASSERT(start2[1]*guess <= (x-guess*start2[0])*DIG_BASE+y);
+ }
+
+ /* 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;
+ buf1=start1+dlen1;
+ for (carry=0; buf2 > start2; buf1--)
+ {
+ ADD(*buf1, *buf1, *--buf2, carry);
+ }
+ for (; buf1 >= start1; buf1--)
+ {
+ SUB2(*buf1, *buf1, 0, carry);
+ }
+ DBUG_ASSERT(carry==1);
+ }
+ *buf0=(dec1)guess;
+ if (*start1 == 0)
+ start1++;
+ }
+ if (mod)
+ {
+ /*
+ now the result is in tmp1, it has
+ intg=prec1-frac1
+ frac=max(frac1, frac2)=to->frac
+ */
+ buf0=to->buf;
+ intg0=ROUND_UP(prec1-frac1)-(start1-tmp1);
+ frac0=ROUND_UP(to->frac);
+ error=E_DEC_OK;
+ if (intg0<=0)
+ {
+ if (unlikely(-intg0 >= to->len))
+ {
+ MAKE_ZERO(to);
+ error=E_DEC_TRUNCATED;
+ goto done;
+ }
+ stop1=start1+frac0;
+ frac0+=intg0;
+ to->intg=0;
+ while (intg0++ < 0)
+ *buf0++=0;
+ }
+ else
+ {
+ if (unlikely(intg0 > to->len))
+ {
+ frac0=0;
+ intg0=to->len;
+ error=E_DEC_OVERFLOW;
+ goto done;
+ }
+ DBUG_ASSERT(intg0 <= ROUND_UP(from2->intg));
+ stop1=start1+frac0+intg0;
+ to->intg=min(intg0*DIG_PER_DEC1, from2->intg);
+ }
+ if (unlikely(intg0+frac0 > to->len))
+ {
+ stop1-=to->len-frac0-intg0;
+ frac0=to->len-intg0;
+ to->frac=frac0*DIG_PER_DEC1;
+ error=E_DEC_TRUNCATED;
+ }
+ while (start1 < stop1)
+ *buf0++=*start1++;
+ }
+done:
+ my_afree(tmp1);
+ return error;
+}
+
+/*
+ division of two decimals
+
+ SYNOPSIS
+ decimal_div()
+ from1 - dividend
+ from2 - divisor
+ to - quotient
+
+ RETURN VALUE
+ E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW/E_DEC_DIV_ZERO;
+
+ NOTES
+ see do_div_mod()
+*/
+
+int decimal_div(decimal *from1, decimal *from2, decimal *to, int scale_incr)
+{
+ return do_div_mod(from1, from2, to, 0, scale_incr);
+}
+
+/*
+ modulus
+
+ SYNOPSIS
+ decimal_mod()
+ from1 - dividend
+ from2 - divisor
+ to - modulus
+
+ RETURN VALUE
+ E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW/E_DEC_DIV_ZERO;
+
+ NOTES
+ see do_div_mod()
+
+ DESCRIPTION
+ the modulus R in R = M mod N
+
+ is defined as
+
+ 0 <= |R| < |M|
+ sign R == sign M
+ R = M - k*N, where k is integer
+
+ thus, there's no requirement for M or N to be integers
+*/
+
+int decimal_mod(decimal *from1, decimal *from2, decimal *to)
+{
+ return do_div_mod(from1, from2, 0, to, 0);
+}
+
+#ifdef MAIN
+
+int full=0;
+decimal a, b, c;
+char buf1[100], buf2[100], buf3[100];
+
+void dump_decimal(decimal *d)
+{
+ int i;
+ printf("/* intg=%d, frac=%d, sign=%d, buf[]={", d->intg, d->frac, d->sign);
+ for (i=0; i < ROUND_UP(d->frac)+ROUND_UP(d->intg)-1; i++)
+ printf("%09d, ", d->buf[i]);
+ printf("%09d} */ ", d->buf[i]);
+}
+
+void print_decimal(decimal *d)
+{
+ char s[100];
+ int slen=sizeof(s);
+
+ if (full) dump_decimal(d);
+ decimal2string(d, s, &slen);
+ printf("'%s'", s);
+}
+
+void test_d2s()
+{
+ char s[100];
+ int slen, res;
+
+ /***********************************/
+ printf("==== decimal2string ====\n");
+ a.buf[0]=12345; a.intg=5; a.frac=0; a.sign=0;
+ slen=sizeof(s);
+ res=decimal2string(&a, s, &slen);
+ dump_decimal(&a); printf(" --> res=%d str='%s' len=%d\n", res, s, slen);
+
+ a.buf[1]=987000000; a.frac=3;
+ slen=sizeof(s);
+ res=decimal2string(&a, s, &slen);
+ dump_decimal(&a); printf(" --> res=%d str='%s' len=%d\n", res, s, slen);
+
+ a.sign=1;
+ slen=sizeof(s);
+ res=decimal2string(&a, s, &slen);
+ dump_decimal(&a); printf(" --> res=%d str='%s' len=%d\n", res, s, slen);
+
+ slen=8;
+ res=decimal2string(&a, s, &slen);
+ dump_decimal(&a); printf(" --> res=%d str='%s' len=%d\n", res, s, slen);
+
+ slen=5;
+ res=decimal2string(&a, s, &slen);
+ dump_decimal(&a); printf(" --> res=%d str='%s' len=%d\n", res, s, slen);
+
+ a.buf[0]=987000000; a.frac=3; a.intg=0;
+ slen=sizeof(s);
+ res=decimal2string(&a, s, &slen);
+ dump_decimal(&a); printf(" --> res=%d str='%s' len=%d\n", res, s, slen);
+}
+
+void test_s2d(char *s)
+{
+ char s1[100];
+ sprintf(s1, "'%s'", s);
+ printf("len=%2d %-30s => res=%d ", a.len, s1, string2decimal(s, &a, 0));
+ print_decimal(&a);
+ printf("\n");
+}
+
+void test_d2f(char *s)
+{
+ char s1[100];
+ double x;
+ int res;
+
+ sprintf(s1, "'%s'", s);
+ string2decimal(s, &a, 0);
+ res=decimal2double(&a, &x);
+ if (full) dump_decimal(&a);
+ printf("%-40s => res=%d %.*g\n", s1, res, a.intg+a.frac, x);
+}
+
+void test_f2d(double from)
+{
+ int res;
+
+ res=double2decimal(from, &a);
+ printf("%-40.*f => res=%d ", DBL_DIG-2, from, res);
+ print_decimal(&a);
+ printf("\n");
+}
+
+void test_ull2d(ulonglong from)
+{
+ char s[100];
+ int res;
+
+ res=ulonglong2decimal(from, &a);
+ longlong10_to_str(from,s,10);
+ printf("%-40s => res=%d ", s, res);
+ print_decimal(&a);
+ printf("\n");
+}
+
+void test_ll2d(longlong from)
+{
+ char s[100];
+ int res;
+
+ res=longlong2decimal(from, &a);
+ longlong10_to_str(from,s,-10);
+ printf("%-40s => res=%d ", s, res);
+ print_decimal(&a);
+ printf("\n");
+}
+
+void test_d2ull(char *s)
+{
+ char s1[100];
+ ulonglong x;
+ int res;
+
+ string2decimal(s, &a, 0);
+ res=decimal2ulonglong(&a, &x);
+ if (full) dump_decimal(&a);
+ longlong10_to_str(x,s1,10);
+ printf("%-40s => res=%d %s\n", s, res, s1);
+}
+
+void test_d2ll(char *s)
+{
+ char s1[100];
+ longlong x;
+ int res;
+
+ string2decimal(s, &a, 0);
+ res=decimal2longlong(&a, &x);
+ if (full) dump_decimal(&a);
+ longlong10_to_str(x,s1,-10);
+ printf("%-40s => res=%d %s\n", s, res, s1);
+}
+
+void test_da(char *s1, char *s2)
+{
+ char s[100];
+ int res;
+ sprintf(s, "'%s' + '%s'", s1, s2);
+ string2decimal(s1, &a, 0);
+ string2decimal(s2, &b, 0);
+ res=decimal_add(&a, &b, &c);
+ printf("%-40s => res=%d ", s, res);
+ print_decimal(&c);
+ printf("\n");
+}
+
+void test_ds(char *s1, char *s2)
+{
+ char s[100];
+ int res;
+ sprintf(s, "'%s' - '%s'", s1, s2);
+ string2decimal(s1, &a, 0);
+ string2decimal(s2, &b, 0);
+ res=decimal_sub(&a, &b, &c);
+ printf("%-40s => res=%d ", s, res);
+ print_decimal(&c);
+ printf("\n");
+}
+
+void test_dm(char *s1, char *s2)
+{
+ char s[100];
+ int res;
+ sprintf(s, "'%s' * '%s'", s1, s2);
+ string2decimal(s1, &a, 0);
+ string2decimal(s2, &b, 0);
+ res=decimal_mul(&a, &b, &c);
+ printf("%-40s => res=%d ", s, res);
+ print_decimal(&c);
+ printf("\n");
+}
+
+void test_dv(char *s1, char *s2)
+{
+ char s[100];
+ int res;
+ sprintf(s, "'%s' / '%s'", s1, s2);
+ string2decimal(s1, &a, 0);
+ string2decimal(s2, &b, 0);
+ res=decimal_div(&a, &b, &c, 5);
+ printf("%-40s => res=%d ", s, res);
+ if (res == E_DEC_DIV_ZERO)
+ printf("E_DEC_DIV_ZERO");
+ else
+ print_decimal(&c);
+ printf("\n");
+}
+
+void test_md(char *s1, char *s2)
+{
+ char s[100];
+ int res;
+ sprintf(s, "'%s' %% '%s'", s1, s2);
+ string2decimal(s1, &a, 0);
+ string2decimal(s2, &b, 0);
+ res=decimal_mod(&a, &b, &c);
+ printf("%-40s => res=%d ", s, res);
+ if (res == E_DEC_DIV_ZERO)
+ printf("E_DEC_DIV_ZERO");
+ else
+ print_decimal(&c);
+ printf("\n");
+}
+
+void test_ro(char *s1, int n, dec_round_mode mode)
+{
+ char s[100];
+ int res;
+ sprintf(s, "%s('%s', %d)", (mode == TRUNCATE ? "truncate" : "round"),
+ s1, n);
+ string2decimal(s1, &a, 0);
+ res=decimal_round(&a, n, mode);
+ printf("%-40s => res=%d ", s, res);
+ print_decimal(&a);
+ printf("\n");
+}
+
+main()
+{
+ a.buf=(void*)buf1;
+ a.len=sizeof(buf1)/sizeof(dec1);
+ b.buf=(void*)buf2;
+ b.len=sizeof(buf2)/sizeof(dec1);
+ c.buf=(void*)buf3;
+ c.len=sizeof(buf3)/sizeof(dec1);
+
+ if (full)
+ test_d2s();
+
+ printf("==== string2decimal ====\n");
+ test_s2d("12345");
+ test_s2d("12345.");
+ test_s2d("123.45");
+ test_s2d("-123.45");
+ test_s2d(".00012345000098765");
+ test_s2d(".12345000098765");
+ test_s2d("-.000000012345000098765");
+ test_s2d("1234500009876.5");
+ a.len=1;
+ test_s2d("123450000098765");
+ test_s2d("123450.000098765");
+ a.len=sizeof(buf1)/sizeof(dec1);
+
+ printf("==== decimal2double ====\n");
+ test_d2f("12345");
+ test_d2f("123.45");
+ test_d2f("-123.45");
+ test_d2f(".00012345000098765");
+ test_d2f("1234500009876.5");
+
+ printf("==== double2decimal ====\n");
+ test_f2d(12345);
+ test_f2d(1.0/3);
+ test_f2d(-123.45);
+ test_f2d(0.00012345000098765);
+ test_f2d(1234500009876.5);
+
+ printf("==== ulonglong2decimal ====\n");
+ test_ull2d(ULL(12345));
+ test_ull2d(ULL(0));
+ test_ull2d(ULL(18446744073709551615));
+
+ printf("==== decimal2ulonglong ====\n");
+ test_d2ull("12345");
+ test_d2ull("0");
+ test_d2ull("18446744073709551615");
+ test_d2ull("18446744073709551616");
+ test_d2ull("-1");
+ test_d2ull("1.23");
+
+ printf("==== longlong2decimal ====\n");
+ test_ll2d(LL(-12345));
+ test_ll2d(LL(-1));
+ test_ll2d(LL(-9223372036854775807));
+ test_ll2d(ULL(9223372036854775808));
+
+ printf("==== decimal2longlong ====\n");
+ test_d2ll("18446744073709551615");
+ test_d2ll("-1");
+ test_d2ll("-1.23");
+ test_d2ll("-9223372036854775807");
+ test_d2ll("-9223372036854775808");
+ test_d2ll("9223372036854775808");
+
+ printf("==== do_add ====\n");
+ test_da(".00012345000098765" ,"123.45");
+ test_da("1234500009876.5" ,".00012345000098765");
+ test_da("9999909999999.5" ,".555");
+ test_da("99999999" ,"1");
+ test_da("989999999" ,"1");
+ test_da("999999999" ,"1");
+ test_da("12345" ,"123.45");
+ test_da("-12345" ,"-123.45");
+ test_ds("-12345" ,"123.45");
+ test_ds("12345" ,"-123.45");
+
+ printf("==== do_sub ====\n");
+ test_ds(".00012345000098765", "123.45");
+ test_ds("1234500009876.5", ".00012345000098765");
+ test_ds("9999900000000.5", ".555");
+ test_ds("1111.5551", "1111.555");
+ test_ds(".555", ".555");
+ test_ds("10000000", "1");
+ test_ds("1000001000", ".1");
+ test_ds("1000000000", ".1");
+ test_ds("12345", "123.45");
+ test_ds("-12345", "-123.45");
+ test_da("-12345", "123.45");
+ test_da("12345", "-123.45");
+ test_ds("123.45", "12345");
+ test_ds("-123.45", "-12345");
+ test_da("123.45", "-12345");
+ test_da("-123.45", "12345");
+
+ printf("==== decimal_mul ====\n");
+ test_dm("12", "10");
+ test_dm("-123.456", "98765.4321");
+ test_dm("-123456000000", "98765432100000");
+ test_dm("123456", "987654321");
+ test_dm("123456", "9876543210");
+ test_dm("123", "0.01");
+ test_dm("123", "0");
+
+ printf("==== decimal_div ====\n");
+ test_dv("120", "10");
+ test_dv("123", "0.01");
+ test_dv("120", "100000000000.00000");
+ test_dv("123", "0");
+ test_dv("-12193185.1853376", "98765.4321");
+ test_dv("121931851853376", "987654321");
+ 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("5678.123451",-4,TRUNCATE);
+ test_ro("5678.123451",-3,TRUNCATE);
+ test_ro("5678.123451",-2,TRUNCATE);
+ test_ro("5678.123451",-1,TRUNCATE);
+ test_ro("5678.123451",0,TRUNCATE);
+ test_ro("5678.123451",1,TRUNCATE);
+ test_ro("5678.123451",2,TRUNCATE);
+ test_ro("5678.123451",3,TRUNCATE);
+ test_ro("5678.123451",4,TRUNCATE);
+ test_ro("5678.123451",5,TRUNCATE);
+ test_ro("5678.123451",6,TRUNCATE);
+
+ printf("==== decimal_mod ====\n");
+ test_md("234","10");
+ test_md("234.567","10.555");
+ test_md("-234.567","10.555");
+ test_md("234.567","-10.555");
+
+ return 0;
+}
+#endif
diff --git a/strings/llstr.c b/strings/llstr.c
index 966b347ac7e..ac034cbe2e2 100644
--- a/strings/llstr.c
+++ b/strings/llstr.c
@@ -30,6 +30,6 @@
char *llstr(longlong value,char *buff)
{
- longlong2str(value,buff,-10);
+ longlong10_to_str(value,buff,-10);
return buff;
}