Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
MariaDB
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexedi
MariaDB
Commits
bd10f962
Commit
bd10f962
authored
Oct 18, 2004
by
serg@serg.mylan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
arbitrary precision decimal numbers
parent
09e9651a
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
1714 additions
and
9 deletions
+1714
-9
.bzrignore
.bzrignore
+1
-0
include/decimal.h
include/decimal.h
+68
-0
strings/Makefile.am
strings/Makefile.am
+17
-8
strings/decimal.c
strings/decimal.c
+1627
-0
strings/llstr.c
strings/llstr.c
+1
-1
No files found.
.bzrignore
View file @
bd10f962
...
@@ -920,3 +920,4 @@ vio/test-ssl
...
@@ -920,3 +920,4 @@ vio/test-ssl
vio/test-sslclient
vio/test-sslclient
vio/test-sslserver
vio/test-sslserver
vio/viotest-ssl
vio/viotest-ssl
strings/test_decimal
include/decimal.h
0 → 100644
View file @
bd10f962
/* 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
strings/Makefile.am
View file @
bd10f962
...
@@ -22,19 +22,19 @@ pkglib_LIBRARIES = libmystrings.a
...
@@ -22,19 +22,19 @@ pkglib_LIBRARIES = libmystrings.a
# Exact one of ASSEMBLER_X
# Exact one of ASSEMBLER_X
if
ASSEMBLER_x86
if
ASSEMBLER_x86
ASRCS
=
strings-x86.s longlong2str-x86.s my_strtoll10-x86.s
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
else
if
ASSEMBLER_sparc32
if
ASSEMBLER_sparc32
# These file MUST all be on the same line!! Otherwise automake
# These file MUST all be on the same line!! Otherwise automake
# generats a very broken makefile
# 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
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
else
#no assembler
#no assembler
ASRCS
=
ASRCS
=
# These file MUST all be on the same line!! Otherwise automake
# These file MUST all be on the same line!! Otherwise automake
# generats a very broken makefile
# 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
endif
endif
...
@@ -43,9 +43,9 @@ noinst_PROGRAMS = conf_to_src
...
@@ -43,9 +43,9 @@ noinst_PROGRAMS = conf_to_src
DISTCLEANFILES
=
ctype_autoconf.c
DISTCLEANFILES
=
ctype_autoconf.c
# Default charset definitions
# Default charset definitions
EXTRA_DIST
=
ctype-big5.c ctype-czech.c ctype-euc_kr.c ctype-win1250ch.c
\
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-gb2312.c ctype-gbk.c ctype-sjis.c ctype-utf8.c
\
ctype-ucs2.c ctype-uca.c ctype-tis620.c ctype-ujis.c
\
ctype-ucs2.c ctype-uca.c ctype-tis620.c ctype-ujis.c
\
xm
l.c strto.c strings-x86.s
\
xml.c decima
l.c strto.c strings-x86.s
\
longlong2str.c longlong2str-x86.s
\
longlong2str.c longlong2str-x86.s
\
my_strtoll10.c my_strtoll10-x86.s
\
my_strtoll10.c my_strtoll10-x86.s
\
strxmov.c bmove_upp.c strappend.c strcont.c strend.c
\
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 \
...
@@ -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
\
bmove_upp-sparc.s strappend-sparc.s strend-sparc.s
\
strinstr-sparc.s strmake-sparc.s strmov-sparc.s
\
strinstr-sparc.s strmake-sparc.s strmov-sparc.s
\
strnmov-sparc.s strstr-sparc.s strxmov-sparc.s
\
strnmov-sparc.s strstr-sparc.s strxmov-sparc.s
\
t_ctype.h
t_ctype.h
libmystrings_a_LIBADD
=
libmystrings_a_LIBADD
=
conf_to_src_SOURCES
=
conf_to_src.c xml.c ctype.c
conf_to_src_SOURCES
=
conf_to_src.c xml.c ctype.c
...
@@ -73,8 +73,17 @@ if ASSEMBLER
...
@@ -73,8 +73,17 @@ if ASSEMBLER
$(AS)
$(ASFLAGS)
-o
$@
$<
$(AS)
$(ASFLAGS)
-o
$@
$<
endif
endif
FLAGS
=
$(DEFS)
$(INCLUDES)
$(CPPFLAGS)
$(CFLAGS)
@NOINST_LDFLAGS@
LIBS
=
libmystrings.a
str_test
:
str_test.c $(LIBRARIES)
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
# Don't update the files from bitkeeper
%
::
SCCS/s.%
%
::
SCCS/s.%
strings/decimal.c
0 → 100644
View file @
bd10f962
/* 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
.
000123450000
98765
);
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
strings/llstr.c
View file @
bd10f962
...
@@ -30,6 +30,6 @@
...
@@ -30,6 +30,6 @@
char
*
llstr
(
longlong
value
,
char
*
buff
)
char
*
llstr
(
longlong
value
,
char
*
buff
)
{
{
longlong
2
str
(
value
,
buff
,
-
10
);
longlong
10_to_
str
(
value
,
buff
,
-
10
);
return
buff
;
return
buff
;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment