summaryrefslogtreecommitdiff
path: root/strings/decimal.c
diff options
context:
space:
mode:
Diffstat (limited to 'strings/decimal.c')
-rw-r--r--strings/decimal.c90
1 files changed, 38 insertions, 52 deletions
diff --git a/strings/decimal.c b/strings/decimal.c
index 6c89657004c..954b04ea446 100644
--- a/strings/decimal.c
+++ b/strings/decimal.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
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
@@ -11,10 +11,7 @@
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-*/
-
-#line 18 "decimal.c"
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/*
=======================================================================
@@ -138,12 +135,6 @@ static const dec1 frac_max[DIG_PER_DEC1-1]={
900000000, 990000000, 999000000,
999900000, 999990000, 999999000,
999999900, 999999990 };
-static double scaler10[]= {
- 1.0, 1e10, 1e20, 1e30, 1e40, 1e50, 1e60, 1e70, 1e80, 1e90
-};
-static double scaler1[]= {
- 1.0, 10.0, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9
-};
#ifdef HAVE_purify
#define sanity(d) DBUG_ASSERT((d)->len > 0)
@@ -257,7 +248,7 @@ void max_decimal(int precision, int frac, decimal_t *to)
}
-static dec1 *remove_leading_zeroes(decimal_t *from, int *intg_result)
+static dec1 *remove_leading_zeroes(const decimal_t *from, int *intg_result)
{
int intg= from->intg, i;
dec1 *buf0= from->buf;
@@ -335,7 +326,7 @@ int decimal_actual_fraction(decimal_t *from)
E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW
*/
-int decimal2string(decimal_t *from, char *to, int *to_len,
+int decimal2string(const decimal_t *from, char *to, int *to_len,
int fixed_precision, int fixed_decimals,
char filler)
{
@@ -949,33 +940,25 @@ fatal_error:
to - result will be stored there
RETURN VALUE
- E_DEC_OK
+ E_DEC_OK/E_DEC_OVERFLOW/E_DEC_TRUNCATED
*/
-int decimal2double(decimal_t *from, double *to)
+int decimal2double(const decimal_t *from, double *to)
{
- double result= 0.0;
- int i, exp= 0;
- dec1 *buf= from->buf;
-
- for (i= from->intg; i > 0; i-= DIG_PER_DEC1)
- result= result * DIG_BASE + *buf++;
-
- for (i= from->frac; i > 0; i-= DIG_PER_DEC1) {
- result= result * DIG_BASE + *buf++;
- exp+= DIG_PER_DEC1;
- }
+ char strbuf[FLOATING_POINT_BUFFER], *end;
+ int len= sizeof(strbuf);
+ int rc, error;
- DBUG_PRINT("info", ("interm.: %f %d %f", result, exp,
- scaler10[exp / 10] * scaler1[exp % 10]));
+ rc = decimal2string(from, strbuf, &len, 0, 0, 0);
+ end= strbuf + len;
- result/= scaler10[exp / 10] * scaler1[exp % 10];
+ DBUG_PRINT("info", ("interm.: %s", strbuf));
- *to= from->sign ? -result : result;
+ *to= my_strtod(strbuf, &end, &error);
DBUG_PRINT("info", ("result: %f", *to));
- return E_DEC_OK;
+ return (rc != E_DEC_OK) ? rc : (error ? E_DEC_OVERFLOW : E_DEC_OK);
}
/*
@@ -992,13 +975,10 @@ int decimal2double(decimal_t *from, double *to)
int double2decimal(double from, decimal_t *to)
{
- /* TODO: fix it, when we'll have dtoa */
- char buff[400], *end;
- int length, res;
+ char buff[FLOATING_POINT_BUFFER], *end;
+ int res;
DBUG_ENTER("double2decimal");
- length= sprintf(buff, "%.16G", from);
- DBUG_PRINT("info",("from: %g from_as_str: %s", from, buff));
- end= buff+length;
+ end= buff + my_gcvt(from, MY_GCVT_ARG_DOUBLE, sizeof(buff) - 1, buff, NULL);
res= string2decimal(buff, to, &end);
DBUG_PRINT("exit", ("res: %d", res));
DBUG_RETURN(res);
@@ -1489,7 +1469,7 @@ int decimal_bin_size(int precision, int scale)
*/
int
-decimal_round(decimal_t *from, decimal_t *to, int scale,
+decimal_round(const decimal_t *from, decimal_t *to, int scale,
decimal_round_mode mode)
{
int frac0=scale>0 ? ROUND_UP(scale) : scale/DIG_PER_DEC1,
@@ -1723,7 +1703,7 @@ int decimal_result_size(decimal_t *from1, decimal_t *from2, char op, int param)
return -1; /* shut up the warning */
}
-static int do_add(decimal_t *from1, decimal_t *from2, decimal_t *to)
+static int do_add(const decimal_t *from1, const decimal_t *from2, decimal_t *to)
{
int intg1=ROUND_UP(from1->intg), intg2=ROUND_UP(from2->intg),
frac1=ROUND_UP(from1->frac), frac2=ROUND_UP(from2->frac),
@@ -1805,7 +1785,7 @@ static int do_add(decimal_t *from1, decimal_t *from2, decimal_t *to)
/* to=from1-from2.
if to==0, return -1/0/+1 - the result of the comparison */
-static int do_sub(decimal_t *from1, decimal_t *from2, decimal_t *to)
+static int do_sub(const decimal_t *from1, const decimal_t *from2, decimal_t *to)
{
int intg1=ROUND_UP(from1->intg), intg2=ROUND_UP(from2->intg),
frac1=ROUND_UP(from1->frac), frac2=ROUND_UP(from2->frac);
@@ -1874,7 +1854,7 @@ static int do_sub(decimal_t *from1, decimal_t *from2, decimal_t *to)
/* ensure that always from1 > from2 (and intg1 >= intg2) */
if (carry)
{
- swap_variables(decimal_t *,from1,from1);
+ swap_variables(const decimal_t *, from1, from2);
swap_variables(dec1 *,start1, start2);
swap_variables(int,intg1,intg2);
swap_variables(int,frac1,frac2);
@@ -1940,35 +1920,35 @@ static int do_sub(decimal_t *from1, decimal_t *from2, decimal_t *to)
return error;
}
-int decimal_intg(decimal_t *from)
+int decimal_intg(const decimal_t *from)
{
int res;
remove_leading_zeroes(from, &res);
return res;
}
-int decimal_add(decimal_t *from1, decimal_t *from2, decimal_t *to)
+int decimal_add(const decimal_t *from1, const decimal_t *from2, decimal_t *to)
{
if (likely(from1->sign == from2->sign))
return do_add(from1, from2, to);
return do_sub(from1, from2, to);
}
-int decimal_sub(decimal_t *from1, decimal_t *from2, decimal_t *to)
+int decimal_sub(const decimal_t *from1, const decimal_t *from2, decimal_t *to)
{
if (likely(from1->sign == from2->sign))
return do_sub(from1, from2, to);
return do_add(from1, from2, to);
}
-int decimal_cmp(decimal_t *from1, decimal_t *from2)
+int decimal_cmp(const decimal_t *from1, const decimal_t *from2)
{
if (likely(from1->sign == from2->sign))
return do_sub(from1, from2, 0);
return from1->sign > from2->sign ? -1 : 1;
}
-int decimal_is_zero(decimal_t *from)
+int decimal_is_zero(const decimal_t *from)
{
dec1 *buf1=from->buf,
*end=buf1+ROUND_UP(from->intg)+ROUND_UP(from->frac);
@@ -1999,7 +1979,7 @@ int decimal_is_zero(decimal_t *from)
XXX if this library is to be used with huge numbers of thousands of
digits, fast multiplication must be implemented.
*/
-int decimal_mul(decimal_t *from1, decimal_t *from2, decimal_t *to)
+int decimal_mul(const decimal_t *from1, const decimal_t *from2, decimal_t *to)
{
int intg1=ROUND_UP(from1->intg), intg2=ROUND_UP(from2->intg),
frac1=ROUND_UP(from1->frac), frac2=ROUND_UP(from2->frac),
@@ -2123,8 +2103,8 @@ int decimal_mul(decimal_t *from1, decimal_t *from2, decimal_t *to)
changed to malloc (or at least fallback to malloc if alloca() fails)
but then, decimal_mul() should be rewritten too :(
*/
-static int do_div_mod(decimal_t *from1, decimal_t *from2,
- decimal_t *to, decimal_t *mod, int scale_incr)
+static int do_div_mod(const decimal_t *from1, const decimal_t *from2,
+ decimal_t *to, decimal_t *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,
@@ -2210,8 +2190,10 @@ static int do_div_mod(decimal_t *from1, decimal_t *from2,
buf0=to->buf;
stop0=buf0+intg0+frac0;
if (likely(div_mod))
- while (dintg++ < 0)
+ while (dintg++ < 0 && buf0 < &to->buf[to->len])
+ {
*buf0++=0;
+ }
len1=(i=ROUND_UP(prec1))+ROUND_UP(2*frac2+scale_incr+1) + 1;
set_if_bigger(len1, 3);
@@ -2301,7 +2283,10 @@ static int do_div_mod(decimal_t *from1, decimal_t *from2,
}
}
if (likely(div_mod))
+ {
+ DBUG_ASSERT(buf0 < to->buf + to->len);
*buf0=(dec1)guess;
+ }
dcarry= *start1;
start1++;
}
@@ -2383,7 +2368,8 @@ done:
*/
int
-decimal_div(decimal_t *from1, decimal_t *from2, decimal_t *to, int scale_incr)
+decimal_div(const decimal_t *from1, const decimal_t *from2, decimal_t *to,
+ int scale_incr)
{
return do_div_mod(from1, from2, to, 0, scale_incr);
}
@@ -2415,7 +2401,7 @@ decimal_div(decimal_t *from1, decimal_t *from2, decimal_t *to, int scale_incr)
thus, there's no requirement for M or N to be integers
*/
-int decimal_mod(decimal_t *from1, decimal_t *from2, decimal_t *to)
+int decimal_mod(const decimal_t *from1, const decimal_t *from2, decimal_t *to)
{
return do_div_mod(from1, from2, 0, to, 0);
}