[Python-checkins] r46471 - sandbox/trunk/decimal-c/Makefile sandbox/trunk/decimal-c/_decimal.c sandbox/trunk/decimal-c/decimal.py
georg.brandl
python-checkins at python.org
Sat May 27 17:38:58 CEST 2006
Author: georg.brandl
Date: Sat May 27 17:38:57 2006
New Revision: 46471
Modified:
sandbox/trunk/decimal-c/Makefile
sandbox/trunk/decimal-c/_decimal.c
sandbox/trunk/decimal-c/decimal.py
Log:
Add (hopefully) helpful comments for Mateusz.
Modified: sandbox/trunk/decimal-c/Makefile
==============================================================================
--- sandbox/trunk/decimal-c/Makefile (original)
+++ sandbox/trunk/decimal-c/Makefile Sat May 27 17:38:57 2006
@@ -1,11 +1,16 @@
# change PYTHON_25 to point to a 2.5 HEAD build
-PYTHON_25=../python-rw/python
+# (a fairly recent HEAD is necessary)
+PYTHON_25=../../../python/python
-all: module test
+all: module run
module:
$(PYTHON_25) setup.py build
cp build/lib*/_decimal.so .
clean:
rm -rf build/
-test:
+test: module
$(PYTHON_25) test_decimal.py
+run: module
+ $(PYTHON_25) -i -c "import _decimal; CD = _decimal.Decimal; import decimal; D = decimal.Decimal"
+debug: module
+ ddd $(PYTHON_25)
Modified: sandbox/trunk/decimal-c/_decimal.c
==============================================================================
--- sandbox/trunk/decimal-c/_decimal.c (original)
+++ sandbox/trunk/decimal-c/_decimal.c Sat May 27 17:38:57 2006
@@ -2,20 +2,65 @@
*
* Partly written for the Need for Speed sprint in
* Iceland by Georg Brandl and Jack Diederich.
+ *
+ * Copyrighted to the Python Software Foundation.
*/
/* Notable incompatibilities between this and the original decimal.py:
- Rounding constants are integers.
- There's no handle method on the exceptions.
+ - Context methods don't accept keyword arguments (they're all
+ called a and b anyways).
- Special values are represented by special sign values, so
the results of as_tuple() differ.
- Many internal "underscore" methods are not accessible on the object.
-*/
+ - In principle, exponents and the number of digits are unbounded in
+ Python decimals, since they could use long integers. While that's
+ praiseworthy, it's also not very speedy ;)
+ - The Python version sometimes gets the context in the middle of
+ a method which leads to some calls succeeding even if there is
+ no valid context.
+
+ Notable kludges necessary retain some compatibility:
+
+ - decimal.py Contexts used to have three dicts, traps, flags and
+ _ignored, to store whether an error occurred or whether to
+ raise an exception in this case. I started with them being bitmasks
+ until we realized that Python code expects them to be dicts and
+ wants to modify those, so we added back dictobjects to decimalobject.
+ I guess that this causes slowdown here and there, and the proper way
+ to do this is to subclass dict to call back some method on assignment.
+ (This is better done in Python code, I guess.)
+ - Some internal methods (names starting with underscores) have been
+ added as a method to the C objects to allow the overlay Python
+ module to use them. They should be removed from that list, along
+ with the wrapper functions parsing the Python arguments, once every-
+ thing is coded in C.
+ - Some special (__xxx__) methods in the Python code took additional
+ arguments used internally. Since that's not possible, there's a
+ _do_decimal_xxx along with a decimal_xxx, see below.
+
+
+ This is not yet optimized C code, but mostly translated Python that
+ was not really made for translating, though it originally should have been.
+
+ Areas which need improvement are marked with XXX throughout the code.
+
+ The functions could use some more comments, I sometimes didn't even transfer
+ every comment in the Python version. Refcounting and more complicated
+ structure make even more comments necessary.
+
+ Each function whose name doesn't start with an underscore is supposed to be
+ stowed in some TypeObject struct.
+
+ Note that the argument and return types of functions are also not very
+ consistent. Casting things around is necessary, but I'm not always sure
+ where the right place for that to happen is. (If anyone calls C a strongly
+ typed language again, hit him on the head.)
-/* This is not yet optimized C code, but mostly translated
- Python that was not really made for translating, though
- it originally should have been.
+ We were undecided whether to offer a public C API (PyDecimal_FromString etc.)
+ If you want to do this, look at the datetime module.
*/
#include "Python.h"
@@ -25,6 +70,7 @@
/* helpful macros ************************************************************/
+/* if we later decide to call this module "squeaky" */
#define MODULE_NAME "_decimal"
#define INITFUNC_NAME init_decimal
@@ -44,7 +90,7 @@
/* Exponent calculations */
-/* XXX: overflow? */
+/* XXX: overflow checking? */
#define ETINY(ctx) ((ctx)->Emin - (ctx)->prec + 1)
#define ETOP(ctx) ((ctx)->Emax - (ctx)->prec + 1)
#define ADJUSTED(dec) ((dec)->exp + (dec)->ob_size - 1)
@@ -53,6 +99,9 @@
/* sign values */
+/* Since we do (decimal->sign & 1) all over the place, it is important
+ that negative signs have the first bit set. */
+
#define SIGN_POS 0
#define SIGN_NEG 1
@@ -82,6 +131,9 @@
/* Context signals */
+/* The naming (conditions vs. signals vs. errors vs. exceptions)
+ is not very consistent. */
+
#define NUMSIGNALS 8
#define S_CLAMPED 0
@@ -95,7 +147,7 @@
/* Other conditions. If they occur and raise an exception,
it will be InvalidOperation. Also, context->flags, ->traps and
- ->ignored will only contain bits for the signals defined above.
+ ->ignored will only contain entries for the signals defined above.
*/
#define NUMCONDITIONS 4
@@ -163,6 +215,8 @@
/* Forwarding ****************************************************************/
+/* some of them could be removed if the functions were declared
+ in the right order */
static contextobject *getcontext(void);
static int setcontext(contextobject *);
static PyObject *context_new(PyTypeObject *, PyObject *, PyObject *);
@@ -181,7 +235,9 @@
/* Exception handlers *********************************************************/
-/* Raise the exception if signal is trapped and not ignored. */
+/* Raise the exception with expl if cond is trapped and not ignored.
+ When raising, return onerror.
+*/
#define HANDLE_ERROR(ctx, cond, expl, onerror) \
int err = (cond >= NUMSIGNALS ? S_INV_OPERATION : cond); \
if (! _is_flag_set(ctx->ignored, err)) { \
@@ -352,6 +408,13 @@
/* Decimal object methods *****************************************************/
+/* NB: "type" is the real type of the object we're creating here.
+
+ The first version didn't take a "type" arg and just used
+ &PyDecimal_DecimalType instead. That's a bad idea since it doesn't allow
+ for subclassing, so we're forced to carry around a typeobject everywhere
+ where we might create new decimalobjects.
+*/
static decimalobject *
_new_decimalobj(PyTypeObject *type, long ndigits, char sign, long exp)
@@ -520,7 +583,7 @@
}
/* Actually round half up. Returns a new reference, either on tmp
- * or a new decimalobject, and steals one reference on tmp in turn
+ * or a new decimalobject, but steals one reference on tmp in turn
* if it was passed in so that you can just return _do_round_half_up(...)
* without DECREFing tmp afterwards.
*/
@@ -623,13 +686,15 @@
return _round_down(self, prec, expdiff, ctx);
}
+/* Mapping rounding constants to functions. Since in C this can be indexed with
+ any value, it's important to check the rounding constants before! */
typedef decimalobject*(*round_func)(decimalobject *, long, long, contextobject *);
static round_func round_funcs[] = {
_round_down, _round_up, _round_half_down, _round_half_even,
_round_half_up, _round_floor, _round_ceiling
};
-/* default: prec=-1, rounding=-1 (use context), see the ROUND_* constants */
+/* default: prec=-1, rounding=-1 (use context values), see the ROUND_* constants */
static decimalobject *
_decimal_round(decimalobject *self, long prec, contextobject *ctx, int rounding)
{
@@ -973,9 +1038,13 @@
return Py_NotImplemented;
}
+/* Often-used context lists. */
static char *ctxkwlist[] = {"context", 0};
static char *decctxkwlist[] = {"other", "context", 0};
+
+/* Make sure that dec is a decimal. Needs to have a contextobject named
+ "ctx" around. */
#define ENSURE_DECIMAL(methname, dec, type) \
dec = (decimalobject *)_convert_to_decimal(type, (PyObject *)dec, ctx, 1); \
if (!dec) { \
@@ -983,6 +1052,8 @@
return NULL; \
}
+/* Make sure that ctx is a valid context. This is commonly used after a
+ PyArg_Parse... which might leave ctx NULL. */
#define ENSURE_CONTEXT(methname, ctx) \
if (ctx == NULL) { \
if (!(ctx = getcontext())) return NULL; \
@@ -991,6 +1062,16 @@
return NULL; \
}
+/* Define some wrappers that parse Python arguments and call the respective
+ C functions. Unfortunately, that's not possible for methods that have a
+ strange signature.
+
+ XXX: should the _do_... functions be declared inline? (there actually
+ is a new macro called Py_LOCAL for this).
+*/
+
+/* Define a Decimal method with one argument (the context).
+ The _do_* version is then called with a valid context. */
#define DECIMAL_UNARY_FUNC(methname) \
static PyObject * \
decimal_##methname(decimalobject *self, PyObject *args, PyObject *kwds) \
@@ -1002,6 +1083,10 @@
return (PyObject *)_do_decimal_##methname(self, ctx); \
}
+/* Define a Decimal method with two arguments (another decimal which might
+ also be an integer and a context).
+ The _do_* version is then called with an actual decimalobject and the
+ context. */
#define DECIMAL_BINARY_FUNC(methname) \
static PyObject * \
decimal_##methname(decimalobject *self, PyObject *args, PyObject *kwds) \
@@ -1021,7 +1106,7 @@
/* Returns -1, 0, 1. No special error return code, must check
- PyErr_Occurred() */
+ for that with PyErr_Occurred(). */
static int
_do_real_decimal_compare(decimalobject *self, decimalobject *other,
contextobject *ctx)
@@ -1172,10 +1257,11 @@
return nan;
}
-
+ /* XXX */
}
DECIMAL_BINARY_FUNC(max)
+
static decimalobject *
_do_decimal_min(decimalobject *self, decimalobject *other,
contextobject *ctx)
@@ -1185,6 +1271,7 @@
}
DECIMAL_BINARY_FUNC(min)
+
/* strip trailing 0s, change anything equal to 0 to 0e0 */
static decimalobject *
_do_decimal_normalize(decimalobject *self, contextobject *ctx)
@@ -1218,8 +1305,8 @@
}
DECIMAL_UNARY_FUNC(normalize)
-/* Quantize self so that its exponent is the same as the exponent of another. */
+/* Quantize self so that its exponent is the same as the exponent of another. */
static decimalobject *
_do_decimal_quantize(decimalobject *self, decimalobject *other,
contextobject *ctx, int rounding, int watchexp)
@@ -1242,6 +1329,7 @@
return _decimal_rescale(self, other->exp, ctx, rounding, watchexp);
}
+
static decimalobject *
decimal_quantize(decimalobject *self, PyObject *args, PyObject *kwds)
{
@@ -1274,6 +1362,7 @@
_do_decimal_remainder_near(decimalobject *self, decimalobject *other,
contextobject *ctx)
{
+ /* XXX */
Py_RETURN_NONE;
}
DECIMAL_BINARY_FUNC(remainder_near)
@@ -1364,6 +1453,7 @@
return ans;
}
+
static PyObject *
decimal_to_integral(decimalobject *self, PyObject *args, PyObject *kwds)
{
@@ -1405,6 +1495,8 @@
return self;
}
+
+/* Contents of Decimal are also immutable. */
static PyObject *
decimal_deepcopy(PyObject *self, PyObject *memo)
{
@@ -1412,8 +1504,8 @@
return self;
}
-/* For internal use, this does what Python code achieves
- with "Decimal(self)". */
+
+/* For internal use; this does what Python code achieves with "Decimal(self)". */
static decimalobject *
_decimal_get_copy(decimalobject *self)
{
@@ -1428,6 +1520,7 @@
return new;
}
+
static PyObject *
decimal_adjusted(decimalobject *self)
{
@@ -1438,6 +1531,7 @@
return PyInt_FromSsize_t(ADJUSTED(self));
}
+
static PyObject *
decimal_as_tuple(decimalobject *self)
{
@@ -1459,12 +1553,15 @@
return res;
}
+
+/* Stringizing and formatting handling */
+
/*
max length of output is len(str(max_long)) + len(str(max_long)) +
len(".") + len("-")
*/
#define FORMAT_SIZE 50
-#define SANITY_CHECK(x) assert((x) < end);
+#define SANITY_CHECK(x) assert((x) < end)
static PyObject *
decimal_special_str(decimalobject *d)
@@ -1501,6 +1598,7 @@
return PyString_FromString(outbuf);
}
+
static PyObject *
decimal_eng_zero_str(decimalobject *d, contextobject *context)
{
@@ -1548,18 +1646,21 @@
return PyString_FromString(p);
}
+
PyObject *
PyDecimal_Str(PyObject *self, PyObject *args, PyObject *kwds)
{
return _do_decimal_str((decimalobject *)self, NULL, 0);
}
+
static PyObject *
decimal_str(decimalobject *d)
{
return _do_decimal_str(d, NULL, 0);
}
+
static PyObject *
_do_decimal_str(decimalobject *d, contextobject *context, int engineering)
{
@@ -1684,9 +1785,9 @@
}
#undef SANITY_CHECK
-/* XXX */
+
static PyObject *
-decimal_round(decimalobject *self, PyObject *args)
+decimal_XXX_round(decimalobject *self, PyObject *args)
{
contextobject *ctx = NULL;
long prec = -1, rnd = -1;
@@ -1774,17 +1875,22 @@
METH_NOARGS},
{"__deepcopy__", decimal_deepcopy,
METH_O},
- /* XXX */
- {"_round", (PyCFunction)decimal_round,
+ /* XXX: only helper methods below */
+ {"_round", (PyCFunction)decimal_XXX_round,
METH_VARARGS},
{"abs__", (PyCFunction)decimal_XXX_abs, METH_VARARGS},
{NULL, NULL}
};
+
static char decimal_doc[] =
PyDoc_STR("Floating point class for decimal arithmetic.");
+/* Declare a "special" (double-underscore) method with two arguments for use
+ in PyNumberMethods slots. As this can take any objects, but other code
+ in this module wants to call some of those methods too, this wrapper
+ is a convenience */
#define DECIMAL_SPECIAL_2FUNC(func) \
static PyObject * \
func (PyObject *self, PyObject *other) { \
@@ -1799,6 +1905,7 @@
return (PyObject *)res; \
}
+/* Same for one-argument methods. */
#define DECIMAL_SPECIAL_1FUNC(func) \
static PyObject * \
func (PyObject *self) { \
@@ -1918,6 +2025,7 @@
_do_decimal_multiply(decimalobject *self, decimalobject *other,
contextobject *ctx)
{
+ /* XXX */
Py_RETURN_NONE;
}
DECIMAL_SPECIAL_2FUNC(decimal_multiply)
@@ -1927,6 +2035,7 @@
_do_decimal_divide(decimalobject *self, decimalobject *other,
contextobject *ctx)
{
+ /* XXX */
Py_RETURN_NONE;
}
DECIMAL_SPECIAL_2FUNC(decimal_divide)
@@ -1936,6 +2045,7 @@
_do_decimal_floor_div(decimalobject *self, decimalobject *other,
contextobject *ctx)
{
+ /* XXX */
Py_RETURN_NONE;
}
DECIMAL_SPECIAL_2FUNC(decimal_floor_div)
@@ -1945,6 +2055,7 @@
_do_decimal_true_div(decimalobject *self, decimalobject *other,
contextobject *ctx)
{
+ /* XXX */
Py_RETURN_NONE;
}
DECIMAL_SPECIAL_2FUNC(decimal_true_div)
@@ -1954,6 +2065,7 @@
_do_decimal_remainder(decimalobject *self, decimalobject *other,
contextobject *ctx)
{
+ /* XXX */
Py_RETURN_NONE;
}
DECIMAL_SPECIAL_2FUNC(decimal_remainder)
@@ -1963,6 +2075,7 @@
_do_decimal_divmod(decimalobject *self, decimalobject *other,
contextobject *ctx)
{
+ /* XXX */
Py_RETURN_NONE;
}
DECIMAL_SPECIAL_2FUNC(decimal_divmod)
@@ -1972,6 +2085,7 @@
_do_decimal_power(decimalobject *self, decimalobject *other,
decimalobject *modulo, contextobject *ctx)
{
+ /* XXX */
Py_RETURN_NONE;
}
@@ -2199,6 +2313,8 @@
}
+/* Trying to avoid casting if not necessary prevents wrong function
+ signatures. */
static PyNumberMethods decimal_as_number = {
decimal_add, /* nb_add */
decimal_subtract, /* nb_subtract */
@@ -2327,11 +2443,13 @@
}
+/* Possible spellings for Infinity. (The first character decides the sign) */
static char *infinities[] = {
"inf", "infinity", "+inf", "+infinity",
"-inf", "-infinity", 0
};
+/* Decimal constructing functions. */
static decimalobject *
_decimal_fromliteralinfinity(PyTypeObject *type, char *str)
@@ -2411,8 +2529,7 @@
goto err;
} while (*++p);
-calculate:
-
+ calculate:
if (dend < len)
exp = atol(str + dend + 1);
if (dpos >= 0)
@@ -2430,7 +2547,7 @@
/* it's a zero */
dend = (dpos == ipos ? ipos+2 : ipos+1);
-finish:
+ finish:
ndigits = dend - ipos - (dpos<0 ? 0 : 1);
new = _new_decimalobj(type, ndigits, sign, exp);
if (!new)
@@ -2443,10 +2560,11 @@
}
return new;
-err:
+ err:
return handle_ConversionSyntax(type, ctx, "invalid literal for Decimal");
}
+
static PyObject *
decimal_from_string(PyTypeObject *type, char *buffer,
long buf_len, contextobject *ctx)
@@ -2464,12 +2582,14 @@
return (PyObject *)_decimal_fromliteral(type, buffer, buf_len, ctx);
}
+
PyObject *
PyDecimal_FromString(char *buffer, long buf_len, contextobject *ctx)
{
return decimal_from_string(&PyDecimal_DecimalType, buffer, buf_len, ctx);
}
+
static PyObject *
decimal_from_long(PyTypeObject *type, long value)
{
@@ -2752,7 +2872,7 @@
}
-/* XXX: just for debugging? */
+/* XXX: just for debugging, or should we keep those? */
static PyMemberDef _decimal_members[] = {
{"_sign", T_INT, offsetof(decimalobject, sign), 0},
{"_exp", T_LONG, offsetof(decimalobject, exp), 0},
@@ -2844,7 +2964,7 @@
&decimal_as_number, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
- (hashfunc)decimal_hash, /* tp_hash */
+ (hashfunc)decimal_hash, /* tp_hash */
0, /* tp_call */
(reprfunc)decimal_str, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
@@ -2878,7 +2998,13 @@
#ifdef WITH_THREAD
-/* Get the context for the current thread. This returns a borrowed reference. */
+/* XXX: No idea if it's actually that easy to get a thread-local context object
+ working. I intended to let Tim Peters double-check this, but now it's
+ your turn ;)
+*/
+
+/* Get the context for the current thread. This returns a borrowed reference
+ since we don't have to DECREF it in about every function then. */
static contextobject *
getcontext(void)
{
@@ -2914,6 +3040,7 @@
return ctx;
}
+
static int
setcontext(contextobject *ctx)
{
@@ -2931,7 +3058,7 @@
return 0;
}
-#else
+#else /* WITH_THREAD */
static PyObject *current_context;
@@ -2966,6 +3093,8 @@
/* Context object ************************************************************/
+/* XXX: This is not safe for subclassing yet. */
+
static contextobject *
_new_contextobj(long prec, int rounding, int rounding_dec, PyObject *traps,
PyObject *flags, long Emin, long Emax, int capitals,
@@ -2974,7 +3103,8 @@
contextobject *new;
PyObject *f = NULL, *t = NULL, *i = NULL;
- new = (contextobject *)PyObject_NEW(contextobject, &PyDecimal_DecimalContextType);
+ new = (contextobject *)PyObject_NEW(contextobject,
+ &PyDecimal_DecimalContextType);
if (new == NULL)
return NULL;
@@ -3040,19 +3170,22 @@
static contextobject *
context_shallow_copy(contextobject *ctx)
{
- return _new_contextobj(ctx->prec, ctx->rounding, ctx->rounding_dec, ctx->traps,
- ctx->flags, ctx->Emin, ctx->Emax, ctx->capitals, ctx->clamp,
- ctx->ignored, 0);
+ return _new_contextobj(ctx->prec, ctx->rounding, ctx->rounding_dec,
+ ctx->traps, ctx->flags, ctx->Emin, ctx->Emax,
+ ctx->capitals, ctx->clamp, ctx->ignored, 0);
}
+
static contextobject *
context_copy(contextobject *ctx)
{
- return _new_contextobj(ctx->prec, ctx->rounding, ctx->rounding_dec, ctx->traps,
- ctx->flags, ctx->Emin, ctx->Emax, ctx->capitals, ctx->clamp,
- ctx->ignored, 1);
+ return _new_contextobj(ctx->prec, ctx->rounding, ctx->rounding_dec,
+ ctx->traps, ctx->flags, ctx->Emin, ctx->Emax,
+ ctx->capitals, ctx->clamp, ctx->ignored, 1);
+ /* Note the difference: ^ */
}
+
static decimalobject *
context_create_decimal(contextobject *self, PyObject *args, PyObject *kwds)
{
@@ -3100,12 +3233,16 @@
return PyInt_FromSsize_t(ETINY(self));
}
+
static PyObject *
context_Etop(contextobject *self)
{
return PyInt_FromSsize_t(ETOP(self));
}
+
+/* Define context functions that just call Decimal methods with a
+ context argument of self. */
#define CONTEXT_UNARY_FUNC(name, decname) \
static PyObject * \
context_##name(contextobject *self, PyObject *a) { \
@@ -3119,6 +3256,7 @@
return res; \
}
+
#define CONTEXT_BINARY_FUNC(name, decname) \
static PyObject * \
context_##name(contextobject *self, PyObject *args) { \
@@ -3137,6 +3275,7 @@
return res; \
}
+
/* helper so that we can use the CONTEXT_UNARY_FUNC macro above */
#define _do_decimal_abs_with_round(a, b) \
_do_decimal_absolute(a, b, 1)
@@ -3296,6 +3435,7 @@
static PyObject *
context_reduce(contextobject *self)
{
+ /* Yes, it seems to be as simple as that. */
return Py_BuildValue("(O(liiOOlliiO))", self->ob_type,
self->prec, self->rounding,
self->rounding_dec, self->traps, self->flags,
@@ -3303,6 +3443,7 @@
self->clamp, self->ignored);
}
+
static PyObject *
context_ignore_flags(contextobject *self, PyObject *args)
{
@@ -3340,6 +3481,7 @@
return ret_flags;
}
+
static PyObject *
context_ignore_all_flags(contextobject *self)
{
@@ -3360,6 +3502,7 @@
return res;
}
+
static PyObject *
context_regard_flags(contextobject *self, PyObject *args)
{
@@ -3392,6 +3535,7 @@
Py_RETURN_NONE;
}
+
static PyObject *
context_set_rounding_decision(contextobject *self, PyObject *args)
{
@@ -3409,6 +3553,7 @@
return PyInt_FromLong((long)old_dec);
}
+
static PyObject *
context_set_rounding(contextobject *self, PyObject *args)
{
@@ -3425,6 +3570,10 @@
return PyInt_FromLong((long)old_round);
}
+
+/* XXX: context_raise_error and friends should really vanish after there's
+ no more Python code calling them. */
+
static PyObject *
_do_context_error_dispatch(int flag, PyObject *args)
{
@@ -3561,6 +3710,16 @@
PyErr_SetString(handler, PyString_AsString(explanation));
return NULL;
}
+
+
+/* XXX: no docstrings here, yet. The doctests of the original Python
+ Context methods must be transferred to some
+ other test module, because keeping them in here is a mess.
+ There might actually be a doctest function that does this.
+*/
+
+/* XXX: all that PyCFunction casting might not be necessary */
+
static PyMethodDef context_methods[] = {
{"clear_flags", (PyCFunction)context_clear_flags,
METH_NOARGS},
@@ -3636,6 +3795,7 @@
{NULL, NULL}
};
+
static char context_doc[] =
PyDoc_STR("Contains the context for a Decimal instance.");
@@ -3649,6 +3809,7 @@
c->ob_type->tp_free(c);
}
+
#define TEST_AND_CAT(num, name) \
if (_is_flag_set(self->flags, num)) { \
strcat(flags, name); \
@@ -3722,6 +3883,7 @@
#undef TEST_AND_CAT
+
static long
context_hash(PyObject *c)
{
@@ -3729,6 +3891,7 @@
return -1;
}
+
static PyObject *
context_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
@@ -3898,6 +4061,9 @@
{NULL}
};
+
+/* XXX: If the dicts stay, Context might need to become a GC type */
+
static PyTypeObject PyDecimal_DecimalContextType = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
@@ -3954,6 +4120,7 @@
return ctx;
}
+
static PyObject *
module_setcontext(PyObject *self, PyObject *args, PyObject *kwds)
{
@@ -3985,6 +4152,7 @@
else Py_RETURN_NONE;
}
+
static PyMethodDef module_methods[] = {
{"getcontext", (PyCFunction)module_getcontext, METH_NOARGS},
{"setcontext", (PyCFunction)module_setcontext, METH_VARARGS | METH_KEYWORDS},
@@ -4136,3 +4304,5 @@
return;
}
+
+/* Yay! */
Modified: sandbox/trunk/decimal-c/decimal.py
==============================================================================
--- sandbox/trunk/decimal-c/decimal.py (original)
+++ sandbox/trunk/decimal-c/decimal.py Sat May 27 17:38:57 2006
@@ -1,119 +1,3 @@
-# Copyright (c) 2004 Python Software Foundation.
-# All rights reserved.
-
-# Written by Eric Price <eprice at tjhsst.edu>
-# and Facundo Batista <facundo at taniquetil.com.ar>
-# and Raymond Hettinger <python at rcn.com>
-# and Aahz <aahz at pobox.com>
-# and Tim Peters
-
-# This module is currently Py2.3 compatible and should be kept that way
-# unless a major compelling advantage arises. IOW, 2.3 compatibility is
-# strongly preferred, but not guaranteed.
-
-# Also, this module should be kept in sync with the latest updates of
-# the IBM specification as it evolves. Those updates will be treated
-# as bug fixes (deviation from the spec is a compatibility, usability
-# bug) and will be backported. At this point the spec is stabilizing
-# and the updates are becoming fewer, smaller, and less significant.
-
-"""
-This is a Py2.3 implementation of decimal floating point arithmetic based on
-the General Decimal Arithmetic Specification:
-
- www2.hursley.ibm.com/decimal/decarith.html
-
-and IEEE standard 854-1987:
-
- www.cs.berkeley.edu/~ejr/projects/754/private/drafts/854-1987/dir.html
-
-Decimal floating point has finite precision with arbitrarily large bounds.
-
-The purpose of the module is to support arithmetic using familiar
-"schoolhouse" rules and to avoid the some of tricky representation
-issues associated with binary floating point. The package is especially
-useful for financial applications or for contexts where users have
-expectations that are at odds with binary floating point (for instance,
-in binary floating point, 1.00 % 0.1 gives 0.09999999999999995 instead
-of the expected Decimal("0.00") returned by decimal floating point).
-
-Here are some examples of using the decimal module:
-
->>> from decimal import *
->>> setcontext(ExtendedContext)
->>> Decimal(0)
-Decimal("0")
->>> Decimal("1")
-Decimal("1")
->>> Decimal("-.0123")
-Decimal("-0.0123")
->>> Decimal(123456)
-Decimal("123456")
->>> Decimal("123.45e12345678901234567890")
-Decimal("1.2345E+12345678901234567892")
->>> Decimal("1.33") + Decimal("1.27")
-Decimal("2.60")
->>> Decimal("12.34") + Decimal("3.87") - Decimal("18.41")
-Decimal("-2.20")
->>> dig = Decimal(1)
->>> print dig / Decimal(3)
-0.333333333
->>> getcontext().prec = 18
->>> print dig / Decimal(3)
-0.333333333333333333
->>> print dig.sqrt()
-1
->>> print Decimal(3).sqrt()
-1.73205080756887729
->>> print Decimal(3) ** 123
-4.85192780976896427E+58
->>> inf = Decimal(1) / Decimal(0)
->>> print inf
-Infinity
->>> neginf = Decimal(-1) / Decimal(0)
->>> print neginf
--Infinity
->>> print neginf + inf
-NaN
->>> print neginf * inf
--Infinity
->>> print dig / 0
-Infinity
->>> getcontext().traps[DivisionByZero] = 1
->>> print dig / 0
-Traceback (most recent call last):
- ...
- ...
- ...
-DivisionByZero: x / 0
->>> c = Context()
->>> c.traps[InvalidOperation] = 0
->>> print c.flags[InvalidOperation]
-0
->>> c.divide(Decimal(0), Decimal(0))
-Decimal("NaN")
->>> c.traps[InvalidOperation] = 1
->>> print c.flags[InvalidOperation]
-1
->>> c.flags[InvalidOperation] = 0
->>> print c.flags[InvalidOperation]
-0
->>> print c.divide(Decimal(0), Decimal(0))
-Traceback (most recent call last):
- ...
- ...
- ...
-InvalidOperation: 0 / 0
->>> print c.flags[InvalidOperation]
-1
->>> c.flags[InvalidOperation] = 0
->>> c.traps[InvalidOperation] = 0
->>> print c.divide(Decimal(0), Decimal(0))
-NaN
->>> print c.flags[InvalidOperation]
-1
->>>
-"""
__all__ = [
# Two major classes
@@ -136,14 +20,7 @@
import copy as _copy
-# #Rounding
-# ROUND_DOWN = 'ROUND_DOWN'
-# ROUND_HALF_UP = 'ROUND_HALF_UP'
-# ROUND_HALF_EVEN = 'ROUND_HALF_EVEN'
-# ROUND_CEILING = 'ROUND_CEILING'
-# ROUND_FLOOR = 'ROUND_FLOOR'
-# ROUND_UP = 'ROUND_UP'
-# ROUND_HALF_DOWN = 'ROUND_HALF_DOWN'
+import _decimal
# #Rounding decision (not part of the public API)
# NEVER_ROUND = 'NEVER_ROUND' # Round in division (non-divmod), sqrt ONLY
@@ -384,89 +261,14 @@
ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_FLOOR, \
ROUND_CEILING, ALWAYS_ROUND, NEVER_ROUND
-##### Context Functions #######################################
-# The getcontext() and setcontext() function manage access to a thread-local
-# current context. Py2.4 offers direct support for thread locals. If that
-# is not available, use threading.currentThread() which is slower but will
-# work for older Pythons. If threads are not part of the build, create a
-# mock threading object with threading.local() returning the module namespace.
-
-#try:
-# import threading
-#except ImportError:
-# # Python was compiled without threads; create a mock object instead
-# import sys
-# class MockThreading:
-# def local(self, sys=sys):
-# return sys.modules[__name__]
-# threading = MockThreading()
-# del sys, MockThreading
-#
-#try:
-# threading.local
-#
-#except AttributeError:
-#
-# #To fix reloading, force it to create a new context
-# #Old contexts have different exceptions in their dicts, making problems.
-# if hasattr(threading.currentThread(), '__decimal_context__'):
-# del threading.currentThread().__decimal_context__
-#
-# def setcontext(context):
-# """Set this thread's context to context."""
-# if context in (DefaultContext, BasicContext, ExtendedContext):
-# context = context.copy()
-# context.clear_flags()
-# threading.currentThread().__decimal_context__ = context
-#
-# def getcontext():
-# """Returns this thread's context.
-#
-# If this thread does not yet have a context, returns
-# a new context and sets this thread's context.
-# New contexts are copies of DefaultContext.
-# """
-# try:
-# return threading.currentThread().__decimal_context__
-# except AttributeError:
-# context = Context()
-# threading.currentThread().__decimal_context__ = context
-# return context
-#
-#else:
-#
-# local = threading.local()
-# if hasattr(local, '__decimal_context__'):
-# del local.__decimal_context__
-#
-# def getcontext(_local=local):
-# """Returns this thread's context.
-#
-# If this thread does not yet have a context, returns
-# a new context and sets this thread's context.
-# New contexts are copies of DefaultContext.
-# """
-# try:
-# return _local.__decimal_context__
-# except AttributeError:
-# context = Context()
-# _local.__decimal_context__ = context
-# return context
-#
-# def setcontext(context, _local=local):
-# """Set this thread's context to context."""
-# if context in (DefaultContext, BasicContext, ExtendedContext):
-# context = context.copy()
-# context.clear_flags()
-# _local.__decimal_context__ = context
-#
-# del threading, local # Don't contaminate the namespace
-#
+# ##### Useful Constants (internal use only) ####################
-##### Decimal class ###########################################
+from _decimal import Inf, negInf, NaN
-import _decimal
+Infsign = (Inf, negInf)
+
+##### Decimal class ###########################################
getcontext = _decimal.getcontext
setcontext = _decimal.setcontext
@@ -474,150 +276,13 @@
class Decimal(_decimal.Decimal):
"""Floating point class for decimal arithmetic."""
- # Generally, the value of the Decimal instance is given by
- # (-1)**_sign * _int * 10**_exp
- # Special values are signified by _is_special == True
-
- # HACK: to get support for converting WorkRep instances
+ # to get support for converting WorkRep instances
def __new__(cls, value="0", context=None):
if isinstance(value, _WorkRep):
value = (value.sign, tuple(map(int, str(value.int))), int(value.exp))
return _decimal.Decimal.__new__(cls, value, context)
-
-# # We're immutable, so use __new__ not __init__
-# def __new__(cls, value="0", context=None):
-# """Create a decimal point instance.
-#
-# >>> Decimal('3.14') # string input
-# Decimal("3.14")
-# >>> Decimal((0, (3, 1, 4), -2)) # tuple input (sign, digit_tuple, exponent)
-# Decimal("3.14")
-# >>> Decimal(314) # int or long
-# Decimal("314")
-# >>> Decimal(Decimal(314)) # another decimal instance
-# Decimal("314")
-# """
-#
-# self = _decimal.Decimal.__new__(cls)
-# self._is_special = False
-#
-# # From an internal working value
-# if isinstance(value, _WorkRep):
-# self._sign = value.sign
-# self._int = tuple(map(int, str(value.int)))
-# self._exp = int(value.exp)
-# return self
-#
-# # From another decimal
-# if isinstance(value, Decimal):
-# self._exp = value._exp
-# self._sign = value._sign
-# self._int = value._int
-# self._is_special = value._is_special
-# return self
-#
-# # From an integer
-# if isinstance(value, (int,long)):
-# if value >= 0:
-# self._sign = 0
-# else:
-# self._sign = 1
-# self._exp = 0
-# self._int = tuple(map(int, str(abs(value))))
-# return self
-#
-# # tuple/list conversion (possibly from as_tuple())
-# if isinstance(value, (list,tuple)):
-# if len(value) != 3:
-# raise ValueError, 'Invalid arguments'
-# if value[0] not in (0,1):
-# raise ValueError, 'Invalid sign'
-# for digit in value[1]:
-# if not isinstance(digit, (int,long)) or digit < 0:
-# raise ValueError, "The second value in the tuple must be composed of non negative integer elements."
-#
-# self._sign = value[0]
-# self._int = tuple(value[1])
-# if value[2] in ('F','n','N'):
-# self._exp = value[2]
-# self._is_special = True
-# else:
-# self._exp = int(value[2])
-# return self
-#
-# if isinstance(value, float):
-# raise TypeError("Cannot convert float to Decimal. " +
-# "First convert the float to a string")
-#
-# # Other argument types may require the context during interpretation
-# if context is None:
-# context = getcontext()
-#
-# # From a string
-# # REs insist on real strings, so we can too.
-# if isinstance(value, basestring):
-# if _isinfinity(value):
-# self._exp = 'F'
-# self._int = (0,)
-# self._is_special = True
-# if _isinfinity(value) == 1:
-# self._sign = 0
-# else:
-# self._sign = 1
-# return self
-# if _isnan(value):
-# sig, sign, diag = _isnan(value)
-# self._is_special = True
-# if len(diag) > context.prec: #Diagnostic info too long
-# self._sign, self._int, self._exp = \
-# context._raise_error(ConversionSyntax)
-# return self
-# if sig == 1:
-# self._exp = 'n' #qNaN
-# else: #sig == 2
-# self._exp = 'N' #sNaN
-# self._sign = sign
-# self._int = tuple(map(int, diag)) #Diagnostic info
-# return self
-# try:
-# self._sign, self._int, self._exp = _string2exact(value)
-# except ValueError:
-# self._is_special = True
-# self._sign, self._int, self._exp = context._raise_error(ConversionSyntax)
-# return self
-#
-# raise TypeError("Cannot convert %r to Decimal" % value)
-
-# def _isnan(self):
-# """Returns whether the number is not actually one.
-
-# 0 if a number
-# 1 if NaN
-# 2 if sNaN
-# """
-# if self._is_special:
-# exp = self._exp
-# if exp == 'n':
-# return 1
-# elif exp == 'N':
-# return 2
-# return 0
-
-# def _isinfinity(self):
-# """Returns whether the number is infinite
-
-# 0 if finite or not a number
-# 1 if +INF
-# -1 if -INF
-# """
-# if self._exp == 'F':
-# if self._sign:
-# return -1
-# return 1
-# return 0
-
def _isnan(self):
if self._sign in (4, 5): return 1
if self._sign in (6, 7): return 2
@@ -658,198 +323,188 @@
return other
return 0
- def __nonzero__(self):
- """Is the number non-zero?
-
- 0 if self == 0
- 1 if self != 0
- """
- if self._is_special:
- return 1
- return sum(self._int) != 0
-
- def __cmp__(self, other, context=None):
- other = _convert_other(other)
- if other is NotImplemented:
- return other
-
- if self._is_special or other._is_special:
- ans = self._check_nans(other, context)
- if ans:
- return 1 # Comparison involving NaN's always reports self > other
-
- # INF = INF
- return cmp(self._isinfinity(), other._isinfinity())
-
- if not self and not other:
- return 0 #If both 0, sign comparison isn't certain.
-
- #If different signs, neg one is less
- if other._sign < self._sign:
- return -1
- if self._sign < other._sign:
- return 1
-
- self_adjusted = self.adjusted()
- other_adjusted = other.adjusted()
- if self_adjusted == other_adjusted and \
- self._int + (0,)*(self._exp - other._exp) == \
- other._int + (0,)*(other._exp - self._exp):
- return 0 #equal, except in precision. ([0]*(-x) = [])
- elif self_adjusted > other_adjusted and self._int[0] != 0:
- return (-1)**self._sign
- elif self_adjusted < other_adjusted and other._int[0] != 0:
- return -((-1)**self._sign)
-
- # Need to round, so make sure we have a valid context
- if context is None:
- context = getcontext()
-
- context = context._shallow_copy()
- rounding = context._set_rounding(ROUND_UP) #round away from 0
-
- flags = context._ignore_all_flags()
- res = self.__sub__(other, context=context)
-
- context._regard_flags(*flags)
-
- context.rounding = rounding
-
- if not res:
- return 0
- elif res._sign:
- return -1
- return 1
-
- def __eq__(self, other):
- if not isinstance(other, (Decimal, int, long)):
- return NotImplemented
- return self.__cmp__(other) == 0
-
- def __ne__(self, other):
- if not isinstance(other, (Decimal, int, long)):
- return NotImplemented
- return self.__cmp__(other) != 0
-
- def compare(self, other, context=None):
- """Compares one to another.
-
- -1 => a < b
- 0 => a = b
- 1 => a > b
- NaN => one is NaN
- Like __cmp__, but returns Decimal instances.
- """
- other = _convert_other(other)
- if other is NotImplemented:
- return other
-
- #compare(NaN, NaN) = NaN
- if (self._is_special or other and other._is_special):
- ans = self._check_nans(other, context)
- if ans:
- return ans
-
- return Decimal(self.__cmp__(other, context))
-
- def __hash__(self):
- """x.__hash__() <==> hash(x)"""
- # Decimal integers must hash the same as the ints
- # Non-integer decimals are normalized and hashed as strings
- # Normalization assures that hash(100E-1) == hash(10)
- if self._is_special:
- if self._isnan():
- raise TypeError('Cannot hash a NaN value.')
- return hash(str(self))
- i = int(self)
- if self == Decimal(i):
- return hash(i)
- assert self.__nonzero__() # '-0' handled by integer case
- return hash(str(self.normalize()))
-
- def as_tuple(self):
- """Represents the number as a triple tuple.
-
- To show the internals exactly as they are.
- """
- return (self._sign, self._int, self._exp)
-
- def __repr__(self):
- """Represents the number as an instance of Decimal."""
- # Invariant: eval(repr(d)) == d
- return 'Decimal("%s")' % str(self)
-
- def __neg__(self, context=None):
- """Returns a copy with the sign switched.
-
- Rounds, if it has reason.
- """
- if self._is_special:
- ans = self._check_nans(context=context)
- if ans:
- return ans
+# def __cmp__(self, other, context=None):
+# other = _convert_other(other)
+# if other is NotImplemented:
+# return other
+
+# if self._is_special or other._is_special:
+# ans = self._check_nans(other, context)
+# if ans:
+# return 1 # Comparison involving NaN's always reports self > other
+
+# # INF = INF
+# return cmp(self._isinfinity(), other._isinfinity())
+
+# if not self and not other:
+# return 0 #If both 0, sign comparison isn't certain.
+
+# #If different signs, neg one is less
+# if other._sign < self._sign:
+# return -1
+# if self._sign < other._sign:
+# return 1
- if not self:
- # -Decimal('0') is Decimal('0'), not Decimal('-0')
- sign = 0
- elif self._sign:
- sign = 0
- else:
- sign = 1
+# self_adjusted = self.adjusted()
+# other_adjusted = other.adjusted()
+# if self_adjusted == other_adjusted and \
+# self._int + (0,)*(self._exp - other._exp) == \
+# other._int + (0,)*(other._exp - self._exp):
+# return 0 #equal, except in precision. ([0]*(-x) = [])
+# elif self_adjusted > other_adjusted and self._int[0] != 0:
+# return (-1)**self._sign
+# elif self_adjusted < other_adjusted and other._int[0] != 0:
+# return -((-1)**self._sign)
+
+# # Need to round, so make sure we have a valid context
+# if context is None:
+# context = getcontext()
+
+# context = context._shallow_copy()
+# rounding = context._set_rounding(ROUND_UP) #round away from 0
+
+# flags = context._ignore_all_flags()
+# res = self.__sub__(other, context=context)
+
+# context._regard_flags(*flags)
+
+# context.rounding = rounding
+
+# if not res:
+# return 0
+# elif res._sign:
+# return -1
+# return 1
+
+# def __eq__(self, other):
+# if not isinstance(other, (Decimal, int, long)):
+# return NotImplemented
+# return self.__cmp__(other) == 0
+
+# def __ne__(self, other):
+# if not isinstance(other, (Decimal, int, long)):
+# return NotImplemented
+# return self.__cmp__(other) != 0
+
+# def compare(self, other, context=None):
+# """Compares one to another.
+
+# -1 => a < b
+# 0 => a = b
+# 1 => a > b
+# NaN => one is NaN
+# Like __cmp__, but returns Decimal instances.
+# """
+# other = _convert_other(other)
+# if other is NotImplemented:
+# return other
+
+# #compare(NaN, NaN) = NaN
+# if (self._is_special or other and other._is_special):
+# ans = self._check_nans(other, context)
+# if ans:
+# return ans
+
+# return Decimal(self.__cmp__(other, context))
+
+# def __hash__(self):
+# """x.__hash__() <==> hash(x)"""
+# # Decimal integers must hash the same as the ints
+# # Non-integer decimals are normalized and hashed as strings
+# # Normalization assures that hash(100E-1) == hash(10)
+# if self._is_special:
+# if self._isnan():
+# raise TypeError('Cannot hash a NaN value.')
+# return hash(str(self))
+# i = int(self)
+# if self == Decimal(i):
+# return hash(i)
+# assert self.__nonzero__() # '-0' handled by integer case
+# return hash(str(self.normalize()))
- if context is None:
- context = getcontext()
- if context._rounding_decision == ALWAYS_ROUND:
- return Decimal((sign, self._int, self._exp))._fix(context)
- return Decimal( (sign, self._int, self._exp))
+# def as_tuple(self):
+# """Represents the number as a triple tuple.
- def __pos__(self, context=None):
- """Returns a copy, unless it is a sNaN.
+# To show the internals exactly as they are.
+# """
+# return (self._sign, self._int, self._exp)
- Rounds the number (if more then precision digits)
- """
- if self._is_special:
- ans = self._check_nans(context=context)
- if ans:
- return ans
+# def __repr__(self):
+# """Represents the number as an instance of Decimal."""
+# # Invariant: eval(repr(d)) == d
+# return 'Decimal("%s")' % str(self)
- sign = self._sign
- if not self:
- # + (-0) = 0
- sign = 0
+# def __neg__(self, context=None):
+# """Returns a copy with the sign switched.
- if context is None:
- context = getcontext()
+# Rounds, if it has reason.
+# """
+# if self._is_special:
+# ans = self._check_nans(context=context)
+# if ans:
+# return ans
+
+# if not self:
+# # -Decimal('0') is Decimal('0'), not Decimal('-0')
+# sign = 0
+# elif self._sign:
+# sign = 0
+# else:
+# sign = 1
+
+# if context is None:
+# context = getcontext()
+# if context._rounding_decision == ALWAYS_ROUND:
+# return Decimal((sign, self._int, self._exp))._fix(context)
+# return Decimal( (sign, self._int, self._exp))
- if context._rounding_decision == ALWAYS_ROUND:
- ans = self._fix(context)
- else:
- ans = Decimal(self)
- ans._sign = sign
- return ans
+# def __pos__(self, context=None):
+# """Returns a copy, unless it is a sNaN.
- def __abs__(self, round=1, context=None):
- """Returns the absolute value of self.
+# Rounds the number (if more then precision digits)
+# """
+# if self._is_special:
+# ans = self._check_nans(context=context)
+# if ans:
+# return ans
+
+# sign = self._sign
+# if not self:
+# # + (-0) = 0
+# sign = 0
+
+# if context is None:
+# context = getcontext()
+
+# if context._rounding_decision == ALWAYS_ROUND:
+# ans = self._fix(context)
+# else:
+# ans = Decimal(self)
+# ans._sign = sign
+# return ans
- If the second argument is 0, do not round.
- """
- if self._is_special:
- ans = self._check_nans(context=context)
- if ans:
- return ans
+# def __abs__(self, round=1, context=None):
+# """Returns the absolute value of self.
- if not round:
- if context is None:
- context = getcontext()
- context = context._shallow_copy()
- context._set_rounding_decision(NEVER_ROUND)
-
- if self._sign:
- ans = self.__neg__(context=context)
- else:
- ans = self.__pos__(context=context)
+# If the second argument is 0, do not round.
+# """
+# if self._is_special:
+# ans = self._check_nans(context=context)
+# if ans:
+# return ans
+
+# if not round:
+# if context is None:
+# context = getcontext()
+# context = context._shallow_copy()
+# context._set_rounding_decision(NEVER_ROUND)
+
+# if self._sign:
+# ans = self.__neg__(context=context)
+# else:
+# ans = self.__pos__(context=context)
- return ans
+# return ans
def __add__(self, other, context=None):
"""Returns self + other.
@@ -1160,7 +815,7 @@
#If we're dividing into ints, and self < other, stop.
#self.__abs__(0) does not round.
- if divmod and (self.__abs__(0, context) < other.__abs__(0, context)):
+ if divmod and (self.abs__(0, context) < other.abs__(0, context)):
if divmod == 1 or divmod == 3:
exp = min(self._exp, other._exp)
@@ -1343,7 +998,7 @@
decrease = not side._iseven()
rounding = context._set_rounding_decision(NEVER_ROUND)
- side = side.__abs__(context=context)
+ side = side.abs__(-1, context)
context._set_rounding_decision(rounding)
s1, s2 = r._sign, comparison._sign
@@ -1375,33 +1030,33 @@
return other
return other.__floordiv__(self, context=context)
- def __float__(self):
- """Float representation."""
- return float(str(self))
+# def __float__(self):
+# """Float representation."""
+# return float(str(self))
- def __int__(self):
- """Converts self to an int, truncating if necessary."""
- if self._is_special:
- if self._isnan():
- context = getcontext()
- return context._raise_error(InvalidContext)
- elif self._isinfinity():
- raise OverflowError, "Cannot convert infinity to long"
- if self._exp >= 0:
- s = ''.join(map(str, self._int)) + '0'*self._exp
- else:
- s = ''.join(map(str, self._int))[:self._exp]
- if s == '':
- s = '0'
- sign = '-'*self._sign
- return int(sign + s)
+# def __int__(self):
+# """Converts self to an int, truncating if necessary."""
+# if self._is_special:
+# if self._isnan():
+# context = getcontext()
+# return context._raise_error(InvalidContext)
+# elif self._isinfinity():
+# raise OverflowError, "Cannot convert infinity to long"
+# if self._exp >= 0:
+# s = ''.join(map(str, self._int)) + '0'*self._exp
+# else:
+# s = ''.join(map(str, self._int))[:self._exp]
+# if s == '':
+# s = '0'
+# sign = '-'*self._sign
+# return int(sign + s)
- def __long__(self):
- """Converts to a long.
+# def __long__(self):
+# """Converts to a long.
- Equivalent to long(int(self))
- """
- return long(self.__int__())
+# Equivalent to long(int(self))
+# """
+# return long(self.__int__())
def _fix(self, context):
"""Round if it is necessary to keep self within prec precision.
@@ -1419,7 +1074,7 @@
prec = context.prec
ans = self._fixexponents(context)
if len(ans._int) > prec:
- ans = ans._round(prec, context=context)
+ ans = ans._round(prec, -1, context)
ans = ans._fixexponents(context)
return ans
@@ -1466,156 +1121,156 @@
return context._raise_error(Overflow, 'above Emax', ans._sign)
return ans
- def _round(self, prec=None, rounding=None, context=None):
- """Returns a rounded version of self.
-
- You can specify the precision or rounding method. Otherwise, the
- context determines it.
- """
-
- if self._is_special:
- ans = self._check_nans(context=context)
- if ans:
- return ans
-
- if self._isinfinity():
- return Decimal(self)
-
- if context is None:
- context = getcontext()
-
- if rounding is None:
- rounding = context.rounding
- if prec is None:
- prec = context.prec
-
- if not self:
- if prec <= 0:
- dig = (0,)
- exp = len(self._int) - prec + self._exp
- else:
- dig = (0,) * prec
- exp = len(self._int) + self._exp - prec
- ans = Decimal((self._sign, dig, exp))
- context._raise_error(Rounded)
- return ans
-
- if prec == 0:
- temp = Decimal(self)
- temp._int = (0,)+temp._int
- prec = 1
- elif prec < 0:
- exp = self._exp + len(self._int) - prec - 1
- temp = Decimal( (self._sign, (0, 1), exp))
- prec = 1
- else:
- temp = Decimal(self)
+# def _round(self, prec=None, rounding=-1, context=None):
+# """Returns a rounded version of self.
- numdigits = len(temp._int)
- if prec == numdigits:
- return temp
-
- # See if we need to extend precision
- expdiff = prec - numdigits
- if expdiff > 0:
- tmp = list(temp._int)
- tmp.extend([0] * expdiff)
- ans = Decimal( (temp._sign, tmp, temp._exp - expdiff))
- return ans
-
- #OK, but maybe all the lost digits are 0.
- lostdigits = self._int[expdiff:]
- if lostdigits == (0,) * len(lostdigits):
- ans = Decimal( (temp._sign, temp._int[:prec], temp._exp - expdiff))
- #Rounded, but not Inexact
- context._raise_error(Rounded)
- return ans
-
- # Okay, let's round and lose data
-
- this_function = getattr(temp, self._pick_rounding_function[rounding])
- #Now we've got the rounding function
-
- if prec != context.prec:
- context = context._shallow_copy()
- context.prec = prec
- ans = this_function(prec, expdiff, context)
- context._raise_error(Rounded)
- context._raise_error(Inexact, 'Changed in rounding')
-
- return ans
-
- _pick_rounding_function = {}
-
- def _round_down(self, prec, expdiff, context):
- """Also known as round-towards-0, truncate."""
- return Decimal( (self._sign, self._int[:prec], self._exp - expdiff) )
-
- def _round_half_up(self, prec, expdiff, context, tmp = None):
- """Rounds 5 up (away from 0)"""
-
- if tmp is None:
- tmp = Decimal( (self._sign,self._int[:prec], self._exp - expdiff))
- if self._int[prec] >= 5:
- tmp = tmp._increment(round=0, context=context)
- if len(tmp._int) > prec:
- return Decimal( (tmp._sign, tmp._int[:-1], tmp._exp + 1))
- return tmp
-
- def _round_half_even(self, prec, expdiff, context):
- """Round 5 to even, rest to nearest."""
-
- tmp = Decimal( (self._sign, self._int[:prec], self._exp - expdiff))
- half = (self._int[prec] == 5)
- if half:
- for digit in self._int[prec+1:]:
- if digit != 0:
- half = 0
- break
- if half:
- if self._int[prec-1] & 1 == 0:
- return tmp
- return self._round_half_up(prec, expdiff, context, tmp)
-
- def _round_half_down(self, prec, expdiff, context):
- """Round 5 down"""
-
- tmp = Decimal( (self._sign, self._int[:prec], self._exp - expdiff))
- half = (self._int[prec] == 5)
- if half:
- for digit in self._int[prec+1:]:
- if digit != 0:
- half = 0
- break
- if half:
- return tmp
- return self._round_half_up(prec, expdiff, context, tmp)
-
- def _round_up(self, prec, expdiff, context):
- """Rounds away from 0."""
- tmp = Decimal( (self._sign, self._int[:prec], self._exp - expdiff) )
- for digit in self._int[prec:]:
- if digit != 0:
- tmp = tmp._increment(round=1, context=context)
- if len(tmp._int) > prec:
- return Decimal( (tmp._sign, tmp._int[:-1], tmp._exp + 1))
- else:
- return tmp
- return tmp
-
- def _round_ceiling(self, prec, expdiff, context):
- """Rounds up (not away from 0 if negative.)"""
- if self._sign:
- return self._round_down(prec, expdiff, context)
- else:
- return self._round_up(prec, expdiff, context)
+# You can specify the precision or rounding method. Otherwise, the
+# context determines it.
+# """
- def _round_floor(self, prec, expdiff, context):
- """Rounds down (not towards 0 if negative)"""
- if not self._sign:
- return self._round_down(prec, expdiff, context)
- else:
- return self._round_up(prec, expdiff, context)
+# if self._is_special:
+# ans = self._check_nans(context=context)
+# if ans:
+# return ans
+
+# if self._isinfinity():
+# return Decimal(self)
+
+# if context is None:
+# context = getcontext()
+
+# if rounding is None:
+# rounding = context.rounding
+# if prec is None:
+# prec = context.prec
+
+# if not self:
+# if prec <= 0:
+# dig = (0,)
+# exp = len(self._int) - prec + self._exp
+# else:
+# dig = (0,) * prec
+# exp = len(self._int) + self._exp - prec
+# ans = Decimal((self._sign, dig, exp))
+# context._raise_error(Rounded)
+# return ans
+
+# if prec == 0:
+# temp = Decimal(self)
+# temp._int = (0,)+temp._int
+# prec = 1
+# elif prec < 0:
+# exp = self._exp + len(self._int) - prec - 1
+# temp = Decimal( (self._sign, (0, 1), exp))
+# prec = 1
+# else:
+# temp = Decimal(self)
+
+# numdigits = len(temp._int)
+# if prec == numdigits:
+# return temp
+
+# # See if we need to extend precision
+# expdiff = prec - numdigits
+# if expdiff > 0:
+# tmp = list(temp._int)
+# tmp.extend([0] * expdiff)
+# ans = Decimal( (temp._sign, tmp, temp._exp - expdiff))
+# return ans
+
+# #OK, but maybe all the lost digits are 0.
+# lostdigits = self._int[expdiff:]
+# if lostdigits == (0,) * len(lostdigits):
+# ans = Decimal( (temp._sign, temp._int[:prec], temp._exp - expdiff))
+# #Rounded, but not Inexact
+# context._raise_error(Rounded)
+# return ans
+
+# # Okay, let's round and lose data
+
+# this_function = getattr(temp, self._pick_rounding_function[rounding])
+# #Now we've got the rounding function
+
+# if prec != context.prec:
+# context = context._shallow_copy()
+# context.prec = prec
+# ans = this_function(prec, expdiff, context)
+# context._raise_error(Rounded)
+# context._raise_error(Inexact, 'Changed in rounding')
+
+# return ans
+
+# _pick_rounding_function = {}
+
+# def _round_down(self, prec, expdiff, context):
+# """Also known as round-towards-0, truncate."""
+# return Decimal( (self._sign, self._int[:prec], self._exp - expdiff) )
+
+# def _round_half_up(self, prec, expdiff, context, tmp = None):
+# """Rounds 5 up (away from 0)"""
+
+# if tmp is None:
+# tmp = Decimal( (self._sign,self._int[:prec], self._exp - expdiff))
+# if self._int[prec] >= 5:
+# tmp = tmp._increment(round=0, context=context)
+# if len(tmp._int) > prec:
+# return Decimal( (tmp._sign, tmp._int[:-1], tmp._exp + 1))
+# return tmp
+
+# def _round_half_even(self, prec, expdiff, context):
+# """Round 5 to even, rest to nearest."""
+
+# tmp = Decimal( (self._sign, self._int[:prec], self._exp - expdiff))
+# half = (self._int[prec] == 5)
+# if half:
+# for digit in self._int[prec+1:]:
+# if digit != 0:
+# half = 0
+# break
+# if half:
+# if self._int[prec-1] & 1 == 0:
+# return tmp
+# return self._round_half_up(prec, expdiff, context, tmp)
+
+# def _round_half_down(self, prec, expdiff, context):
+# """Round 5 down"""
+
+# tmp = Decimal( (self._sign, self._int[:prec], self._exp - expdiff))
+# half = (self._int[prec] == 5)
+# if half:
+# for digit in self._int[prec+1:]:
+# if digit != 0:
+# half = 0
+# break
+# if half:
+# return tmp
+# return self._round_half_up(prec, expdiff, context, tmp)
+
+# def _round_up(self, prec, expdiff, context):
+# """Rounds away from 0."""
+# tmp = Decimal( (self._sign, self._int[:prec], self._exp - expdiff) )
+# for digit in self._int[prec:]:
+# if digit != 0:
+# tmp = tmp._increment(round=1, context=context)
+# if len(tmp._int) > prec:
+# return Decimal( (tmp._sign, tmp._int[:-1], tmp._exp + 1))
+# else:
+# return tmp
+# return tmp
+
+# def _round_ceiling(self, prec, expdiff, context):
+# """Rounds up (not away from 0 if negative.)"""
+# if self._sign:
+# return self._round_down(prec, expdiff, context)
+# else:
+# return self._round_up(prec, expdiff, context)
+
+# def _round_floor(self, prec, expdiff, context):
+# """Rounds down (not towards 0 if negative)"""
+# if not self._sign:
+# return self._round_down(prec, expdiff, context)
+# else:
+# return self._round_up(prec, expdiff, context)
def __pow__(self, n, modulo = None, context=None):
"""Return self ** n (mod modulo)
@@ -1715,60 +1370,60 @@
return other
return other.__pow__(self, context=context)
- def normalize(self, context=None):
- """Normalize- strip trailing 0s, change anything equal to 0 to 0e0"""
-
- if self._is_special:
- ans = self._check_nans(context=context)
- if ans:
- return ans
-
- dup = self._fix(context)
- if dup._isinfinity():
- return dup
-
- if not dup:
- return Decimal( (dup._sign, (0,), 0) )
- end = len(dup._int)
- exp = dup._exp
- while dup._int[end-1] == 0:
- exp += 1
- end -= 1
- return Decimal( (dup._sign, dup._int[:end], exp) )
+# def normalize(self, context=None):
+# """Normalize- strip trailing 0s, change anything equal to 0 to 0e0"""
+# if self._is_special:
+# ans = self._check_nans(context=context)
+# if ans:
+# return ans
+
+# dup = self._fix(context)
+# if dup._isinfinity():
+# return dup
+
+# if not dup:
+# return Decimal( (dup._sign, (0,), 0) )
+# end = len(dup._int)
+# exp = dup._exp
+# while dup._int[end-1] == 0:
+# exp += 1
+# end -= 1
+# return Decimal( (dup._sign, dup._int[:end], exp) )
- def quantize(self, exp, rounding=None, context=None, watchexp=1):
- """Quantize self so its exponent is the same as that of exp.
- Similar to self._rescale(exp._exp) but with error checking.
- """
- if self._is_special or exp._is_special:
- ans = self._check_nans(exp, context)
- if ans:
- return ans
+# def quantize(self, exp, rounding=-1, context=None, watchexp=1):
+# """Quantize self so its exponent is the same as that of exp.
- if exp._isinfinity() or self._isinfinity():
- if exp._isinfinity() and self._isinfinity():
- return self #if both are inf, it is OK
- if context is None:
- context = getcontext()
- return context._raise_error(InvalidOperation,
- 'quantize with one INF')
- return self._rescale(exp._exp, rounding, context, watchexp)
+# Similar to self._rescale(exp._exp) but with error checking.
+# """
+# if self._is_special or exp._is_special:
+# ans = self._check_nans(exp, context)
+# if ans:
+# return ans
+
+# if exp._isinfinity() or self._isinfinity():
+# if exp._isinfinity() and self._isinfinity():
+# return self #if both are inf, it is OK
+# if context is None:
+# context = getcontext()
+# return context._raise_error(InvalidOperation,
+# 'quantize with one INF')
+# return self._rescale(exp._exp, rounding, context, watchexp)
- def same_quantum(self, other):
- """Test whether self and other have the same exponent.
+# def same_quantum(self, other):
+# """Test whether self and other have the same exponent.
- same as self._exp == other._exp, except NaN == sNaN
- """
- if self._is_special or other._is_special:
- if self._isnan() or other._isnan():
- return self._isnan() and other._isnan() and True
- if self._isinfinity() or other._isinfinity():
- return self._isinfinity() and other._isinfinity() and True
- return self._exp == other._exp
+# same as self._exp == other._exp, except NaN == sNaN
+# """
+# if self._is_special or other._is_special:
+# if self._isnan() or other._isnan():
+# return self._isnan() and other._isnan() and True
+# if self._isinfinity() or other._isinfinity():
+# return self._isinfinity() and other._isinfinity() and True
+# return self._exp == other._exp
- def _rescale(self, exp, rounding=None, context=None, watchexp=1):
+ def _rescale(self, exp, rounding=-1, context=None, watchexp=1):
"""Rescales so that the exponent is exp.
exp = exp to scale to (an integer)
@@ -1810,7 +1465,7 @@
tmp._exp = -digits + tmp._exp
tmp._int = (0,1)
digits = 1
- tmp = tmp._round(digits, rounding, context=context)
+ tmp = tmp._round(digits, rounding, context)
if tmp._int[0] == 0 and len(tmp._int) > 1:
tmp._int = tmp._int[1:]
@@ -1823,20 +1478,20 @@
return context._raise_error(InvalidOperation, 'rescale(a, INF)')
return tmp
- def to_integral(self, rounding=None, context=None):
- """Rounds to the nearest integer, without raising inexact, rounded."""
- if self._is_special:
- ans = self._check_nans(context=context)
- if ans:
- return ans
- if self._exp >= 0:
- return self
- if context is None:
- context = getcontext()
- flags = context._ignore_flags(Rounded, Inexact)
- ans = self._rescale(0, rounding, context=context)
- context._regard_flags(flags)
- return ans
+ #def to_integral(self, rounding=None, context=None):
+ # """Rounds to the nearest integer, without raising inexact, rounded."""
+ # if self._is_special:
+ # ans = self._check_nans(context=context)
+ # if ans:
+ # return ans
+ # if self._exp >= 0:
+ # return self
+ # if context is None:
+ # context = getcontext()
+ # flags = context._ignore_flags(Rounded, Inexact)
+ # ans = self._rescale(0, rounding, context=context)
+ # context._regard_flags(flags)
+ # return ans
def sqrt(self, context=None):
"""Return the square root of self.
@@ -2071,40 +1726,28 @@
return 1
return self._int[-1+self._exp] & 1 == 0
- def adjusted(self):
- """Return the adjusted exponent of self"""
- try:
- return self._exp + len(self._int) - 1
- #If NaN or Infinity, self._exp is string
- except TypeError:
- return 0
+# def adjusted(self):
+# """Return the adjusted exponent of self"""
+# try:
+# return self._exp + len(self._int) - 1
+# #If NaN or Infinity, self._exp is string
+# except TypeError:
+# return 0
- # support for pickling, copy, and deepcopy
- def __reduce__(self):
- return (self.__class__, (str(self),))
-
- def __copy__(self):
- if type(self) == Decimal:
- return self # I'm immutable; therefore I am my own clone
- return self.__class__(str(self))
-
- def __deepcopy__(self, memo):
- if type(self) == Decimal:
- return self # My components are also immutable
- return self.__class__(str(self))
##### Context class ###########################################
+from _decimal import Context
-# get rounding method function:
-rounding_functions = [name for name in Decimal.__dict__.keys() if name.startswith('_round_')]
-for name in rounding_functions:
- #name is like _round_half_even, goes to the global ROUND_HALF_EVEN value.
- globalname = name[1:].upper()
- val = globals()[globalname]
- Decimal._pick_rounding_function[val] = name
+# # get rounding method function:
+# rounding_functions = [name for name in Decimal.__dict__.keys() if name.startswith('_round_')]
+# for name in rounding_functions:
+# #name is like _round_half_even, goes to the global ROUND_HALF_EVEN value.
+# globalname = name[1:].upper()
+# val = globals()[globalname]
+# Decimal._pick_rounding_function[val] = name
-del name, val, globalname, rounding_functions
+# del name, val, globalname, rounding_functions
class ContextManager(object):
"""Helper class to simplify Context management.
@@ -2130,504 +1773,6 @@
def __exit__(self, t, v, tb):
setcontext(self.saved_context)
-class Context(_decimal.Context):
- """Contains the context for a Decimal instance.
-
- Contains:
- prec - precision (for use in rounding, division, square roots..)
- rounding - rounding type. (how you round)
- _rounding_decision - ALWAYS_ROUND, NEVER_ROUND -- do you round?
- traps - If traps[exception] = 1, then the exception is
- raised when it is caused. Otherwise, a value is
- substituted in.
- flags - When an exception is caused, flags[exception] is incremented.
- (Whether or not the trap_enabler is set)
- Should be reset by user of Decimal instance.
- Emin - Minimum exponent
- Emax - Maximum exponent
- capitals - If 1, 1*10^1 is printed as 1E+1.
- If 0, printed as 1e1
- _clamp - If 1, change exponents if too high (Default 0)
- """
-
- def __repr__(self):
- """Show the current context."""
- s = []
- s.append('Context(prec=%(prec)d, rounding=%(rounding)s, Emin=%(Emin)d, Emax=%(Emax)d, capitals=%(capitals)d' % vars(self))
- s.append('flags=[' + ', '.join([f.__name__ for f, v in self.flags.items() if v]) + ']')
- s.append('traps=[' + ', '.join([t.__name__ for t, v in self.traps.items() if v]) + ']')
- return ', '.join(s) + ')'
-
- def create_decimal(self, num='0'):
- """Creates a new Decimal instance but using self as context."""
- d = Decimal(num, context=self)
- return d._fix(self)
-
- #Methods
- def abs(self, a):
- """Returns the absolute value of the operand.
-
- If the operand is negative, the result is the same as using the minus
- operation on the operand. Otherwise, the result is the same as using
- the plus operation on the operand.
-
- >>> ExtendedContext.abs(Decimal('2.1'))
- Decimal("2.1")
- >>> ExtendedContext.abs(Decimal('-100'))
- Decimal("100")
- >>> ExtendedContext.abs(Decimal('101.5'))
- Decimal("101.5")
- >>> ExtendedContext.abs(Decimal('-101.5'))
- Decimal("101.5")
- """
- return a.__abs__(context=self)
-
- def add(self, a, b):
- """Return the sum of the two operands.
-
- >>> ExtendedContext.add(Decimal('12'), Decimal('7.00'))
- Decimal("19.00")
- >>> ExtendedContext.add(Decimal('1E+2'), Decimal('1.01E+4'))
- Decimal("1.02E+4")
- """
- return a.__add__(b, context=self)
-
- def _apply(self, a):
- return str(a._fix(self))
-
- def compare(self, a, b):
- """Compares values numerically.
-
- If the signs of the operands differ, a value representing each operand
- ('-1' if the operand is less than zero, '0' if the operand is zero or
- negative zero, or '1' if the operand is greater than zero) is used in
- place of that operand for the comparison instead of the actual
- operand.
-
- The comparison is then effected by subtracting the second operand from
- the first and then returning a value according to the result of the
- subtraction: '-1' if the result is less than zero, '0' if the result is
- zero or negative zero, or '1' if the result is greater than zero.
-
- >>> ExtendedContext.compare(Decimal('2.1'), Decimal('3'))
- Decimal("-1")
- >>> ExtendedContext.compare(Decimal('2.1'), Decimal('2.1'))
- Decimal("0")
- >>> ExtendedContext.compare(Decimal('2.1'), Decimal('2.10'))
- Decimal("0")
- >>> ExtendedContext.compare(Decimal('3'), Decimal('2.1'))
- Decimal("1")
- >>> ExtendedContext.compare(Decimal('2.1'), Decimal('-3'))
- Decimal("1")
- >>> ExtendedContext.compare(Decimal('-3'), Decimal('2.1'))
- Decimal("-1")
- """
- return a.compare(b, context=self)
-
- def divide(self, a, b):
- """Decimal division in a specified context.
-
- >>> ExtendedContext.divide(Decimal('1'), Decimal('3'))
- Decimal("0.333333333")
- >>> ExtendedContext.divide(Decimal('2'), Decimal('3'))
- Decimal("0.666666667")
- >>> ExtendedContext.divide(Decimal('5'), Decimal('2'))
- Decimal("2.5")
- >>> ExtendedContext.divide(Decimal('1'), Decimal('10'))
- Decimal("0.1")
- >>> ExtendedContext.divide(Decimal('12'), Decimal('12'))
- Decimal("1")
- >>> ExtendedContext.divide(Decimal('8.00'), Decimal('2'))
- Decimal("4.00")
- >>> ExtendedContext.divide(Decimal('2.400'), Decimal('2.0'))
- Decimal("1.20")
- >>> ExtendedContext.divide(Decimal('1000'), Decimal('100'))
- Decimal("10")
- >>> ExtendedContext.divide(Decimal('1000'), Decimal('1'))
- Decimal("1000")
- >>> ExtendedContext.divide(Decimal('2.40E+6'), Decimal('2'))
- Decimal("1.20E+6")
- """
- return a.__div__(b, context=self)
-
- def divide_int(self, a, b):
- """Divides two numbers and returns the integer part of the result.
-
- >>> ExtendedContext.divide_int(Decimal('2'), Decimal('3'))
- Decimal("0")
- >>> ExtendedContext.divide_int(Decimal('10'), Decimal('3'))
- Decimal("3")
- >>> ExtendedContext.divide_int(Decimal('1'), Decimal('0.3'))
- Decimal("3")
- """
- return a.__floordiv__(b, context=self)
-
- def divmod(self, a, b):
- return a.__divmod__(b, context=self)
-
- def max(self, a,b):
- """max compares two values numerically and returns the maximum.
-
- If either operand is a NaN then the general rules apply.
- Otherwise, the operands are compared as as though by the compare
- operation. If they are numerically equal then the left-hand operand
- is chosen as the result. Otherwise the maximum (closer to positive
- infinity) of the two operands is chosen as the result.
-
- >>> ExtendedContext.max(Decimal('3'), Decimal('2'))
- Decimal("3")
- >>> ExtendedContext.max(Decimal('-10'), Decimal('3'))
- Decimal("3")
- >>> ExtendedContext.max(Decimal('1.0'), Decimal('1'))
- Decimal("1")
- >>> ExtendedContext.max(Decimal('7'), Decimal('NaN'))
- Decimal("7")
- """
- return a.max(b, context=self)
-
- def min(self, a,b):
- """min compares two values numerically and returns the minimum.
-
- If either operand is a NaN then the general rules apply.
- Otherwise, the operands are compared as as though by the compare
- operation. If they are numerically equal then the left-hand operand
- is chosen as the result. Otherwise the minimum (closer to negative
- infinity) of the two operands is chosen as the result.
-
- >>> ExtendedContext.min(Decimal('3'), Decimal('2'))
- Decimal("2")
- >>> ExtendedContext.min(Decimal('-10'), Decimal('3'))
- Decimal("-10")
- >>> ExtendedContext.min(Decimal('1.0'), Decimal('1'))
- Decimal("1.0")
- >>> ExtendedContext.min(Decimal('7'), Decimal('NaN'))
- Decimal("7")
- """
- return a.min(b, context=self)
-
- def minus(self, a):
- """Minus corresponds to unary prefix minus in Python.
-
- The operation is evaluated using the same rules as subtract; the
- operation minus(a) is calculated as subtract('0', a) where the '0'
- has the same exponent as the operand.
-
- >>> ExtendedContext.minus(Decimal('1.3'))
- Decimal("-1.3")
- >>> ExtendedContext.minus(Decimal('-1.3'))
- Decimal("1.3")
- """
- return a.__neg__(context=self)
-
- def multiply(self, a, b):
- """multiply multiplies two operands.
-
- If either operand is a special value then the general rules apply.
- Otherwise, the operands are multiplied together ('long multiplication'),
- resulting in a number which may be as long as the sum of the lengths
- of the two operands.
-
- >>> ExtendedContext.multiply(Decimal('1.20'), Decimal('3'))
- Decimal("3.60")
- >>> ExtendedContext.multiply(Decimal('7'), Decimal('3'))
- Decimal("21")
- >>> ExtendedContext.multiply(Decimal('0.9'), Decimal('0.8'))
- Decimal("0.72")
- >>> ExtendedContext.multiply(Decimal('0.9'), Decimal('-0'))
- Decimal("-0.0")
- >>> ExtendedContext.multiply(Decimal('654321'), Decimal('654321'))
- Decimal("4.28135971E+11")
- """
- return a.__mul__(b, context=self)
-
- def normalize(self, a):
- """normalize reduces an operand to its simplest form.
-
- Essentially a plus operation with all trailing zeros removed from the
- result.
-
- >>> ExtendedContext.normalize(Decimal('2.1'))
- Decimal("2.1")
- >>> ExtendedContext.normalize(Decimal('-2.0'))
- Decimal("-2")
- >>> ExtendedContext.normalize(Decimal('1.200'))
- Decimal("1.2")
- >>> ExtendedContext.normalize(Decimal('-120'))
- Decimal("-1.2E+2")
- >>> ExtendedContext.normalize(Decimal('120.00'))
- Decimal("1.2E+2")
- >>> ExtendedContext.normalize(Decimal('0.00'))
- Decimal("0")
- """
- return a.normalize(context=self)
-
- def plus(self, a):
- """Plus corresponds to unary prefix plus in Python.
-
- The operation is evaluated using the same rules as add; the
- operation plus(a) is calculated as add('0', a) where the '0'
- has the same exponent as the operand.
-
- >>> ExtendedContext.plus(Decimal('1.3'))
- Decimal("1.3")
- >>> ExtendedContext.plus(Decimal('-1.3'))
- Decimal("-1.3")
- """
- return a.__pos__(context=self)
-
- def power(self, a, b, modulo=None):
- """Raises a to the power of b, to modulo if given.
-
- The right-hand operand must be a whole number whose integer part (after
- any exponent has been applied) has no more than 9 digits and whose
- fractional part (if any) is all zeros before any rounding. The operand
- may be positive, negative, or zero; if negative, the absolute value of
- the power is used, and the left-hand operand is inverted (divided into
- 1) before use.
-
- If the increased precision needed for the intermediate calculations
- exceeds the capabilities of the implementation then an Invalid operation
- condition is raised.
-
- If, when raising to a negative power, an underflow occurs during the
- division into 1, the operation is not halted at that point but
- continues.
-
- >>> ExtendedContext.power(Decimal('2'), Decimal('3'))
- Decimal("8")
- >>> ExtendedContext.power(Decimal('2'), Decimal('-3'))
- Decimal("0.125")
- >>> ExtendedContext.power(Decimal('1.7'), Decimal('8'))
- Decimal("69.7575744")
- >>> ExtendedContext.power(Decimal('Infinity'), Decimal('-2'))
- Decimal("0")
- >>> ExtendedContext.power(Decimal('Infinity'), Decimal('-1'))
- Decimal("0")
- >>> ExtendedContext.power(Decimal('Infinity'), Decimal('0'))
- Decimal("1")
- >>> ExtendedContext.power(Decimal('Infinity'), Decimal('1'))
- Decimal("Infinity")
- >>> ExtendedContext.power(Decimal('Infinity'), Decimal('2'))
- Decimal("Infinity")
- >>> ExtendedContext.power(Decimal('-Infinity'), Decimal('-2'))
- Decimal("0")
- >>> ExtendedContext.power(Decimal('-Infinity'), Decimal('-1'))
- Decimal("-0")
- >>> ExtendedContext.power(Decimal('-Infinity'), Decimal('0'))
- Decimal("1")
- >>> ExtendedContext.power(Decimal('-Infinity'), Decimal('1'))
- Decimal("-Infinity")
- >>> ExtendedContext.power(Decimal('-Infinity'), Decimal('2'))
- Decimal("Infinity")
- >>> ExtendedContext.power(Decimal('0'), Decimal('0'))
- Decimal("NaN")
- """
- return a.__pow__(b, modulo, context=self)
-
- def quantize(self, a, b):
- """Returns a value equal to 'a' (rounded) and having the exponent of 'b'.
-
- The coefficient of the result is derived from that of the left-hand
- operand. It may be rounded using the current rounding setting (if the
- exponent is being increased), multiplied by a positive power of ten (if
- the exponent is being decreased), or is unchanged (if the exponent is
- already equal to that of the right-hand operand).
-
- Unlike other operations, if the length of the coefficient after the
- quantize operation would be greater than precision then an Invalid
- operation condition is raised. This guarantees that, unless there is an
- error condition, the exponent of the result of a quantize is always
- equal to that of the right-hand operand.
-
- Also unlike other operations, quantize will never raise Underflow, even
- if the result is subnormal and inexact.
-
- >>> ExtendedContext.quantize(Decimal('2.17'), Decimal('0.001'))
- Decimal("2.170")
- >>> ExtendedContext.quantize(Decimal('2.17'), Decimal('0.01'))
- Decimal("2.17")
- >>> ExtendedContext.quantize(Decimal('2.17'), Decimal('0.1'))
- Decimal("2.2")
- >>> ExtendedContext.quantize(Decimal('2.17'), Decimal('1e+0'))
- Decimal("2")
- >>> ExtendedContext.quantize(Decimal('2.17'), Decimal('1e+1'))
- Decimal("0E+1")
- >>> ExtendedContext.quantize(Decimal('-Inf'), Decimal('Infinity'))
- Decimal("-Infinity")
- >>> ExtendedContext.quantize(Decimal('2'), Decimal('Infinity'))
- Decimal("NaN")
- >>> ExtendedContext.quantize(Decimal('-0.1'), Decimal('1'))
- Decimal("-0")
- >>> ExtendedContext.quantize(Decimal('-0'), Decimal('1e+5'))
- Decimal("-0E+5")
- >>> ExtendedContext.quantize(Decimal('+35236450.6'), Decimal('1e-2'))
- Decimal("NaN")
- >>> ExtendedContext.quantize(Decimal('-35236450.6'), Decimal('1e-2'))
- Decimal("NaN")
- >>> ExtendedContext.quantize(Decimal('217'), Decimal('1e-1'))
- Decimal("217.0")
- >>> ExtendedContext.quantize(Decimal('217'), Decimal('1e-0'))
- Decimal("217")
- >>> ExtendedContext.quantize(Decimal('217'), Decimal('1e+1'))
- Decimal("2.2E+2")
- >>> ExtendedContext.quantize(Decimal('217'), Decimal('1e+2'))
- Decimal("2E+2")
- """
- return a.quantize(b, context=self)
-
- def remainder(self, a, b):
- """Returns the remainder from integer division.
-
- The result is the residue of the dividend after the operation of
- calculating integer division as described for divide-integer, rounded to
- precision digits if necessary. The sign of the result, if non-zero, is
- the same as that of the original dividend.
-
- This operation will fail under the same conditions as integer division
- (that is, if integer division on the same two operands would fail, the
- remainder cannot be calculated).
-
- >>> ExtendedContext.remainder(Decimal('2.1'), Decimal('3'))
- Decimal("2.1")
- >>> ExtendedContext.remainder(Decimal('10'), Decimal('3'))
- Decimal("1")
- >>> ExtendedContext.remainder(Decimal('-10'), Decimal('3'))
- Decimal("-1")
- >>> ExtendedContext.remainder(Decimal('10.2'), Decimal('1'))
- Decimal("0.2")
- >>> ExtendedContext.remainder(Decimal('10'), Decimal('0.3'))
- Decimal("0.1")
- >>> ExtendedContext.remainder(Decimal('3.6'), Decimal('1.3'))
- Decimal("1.0")
- """
- return a.__mod__(b, context=self)
-
- def remainder_near(self, a, b):
- """Returns to be "a - b * n", where n is the integer nearest the exact
- value of "x / b" (if two integers are equally near then the even one
- is chosen). If the result is equal to 0 then its sign will be the
- sign of a.
-
- This operation will fail under the same conditions as integer division
- (that is, if integer division on the same two operands would fail, the
- remainder cannot be calculated).
-
- >>> ExtendedContext.remainder_near(Decimal('2.1'), Decimal('3'))
- Decimal("-0.9")
- >>> ExtendedContext.remainder_near(Decimal('10'), Decimal('6'))
- Decimal("-2")
- >>> ExtendedContext.remainder_near(Decimal('10'), Decimal('3'))
- Decimal("1")
- >>> ExtendedContext.remainder_near(Decimal('-10'), Decimal('3'))
- Decimal("-1")
- >>> ExtendedContext.remainder_near(Decimal('10.2'), Decimal('1'))
- Decimal("0.2")
- >>> ExtendedContext.remainder_near(Decimal('10'), Decimal('0.3'))
- Decimal("0.1")
- >>> ExtendedContext.remainder_near(Decimal('3.6'), Decimal('1.3'))
- Decimal("-0.3")
- """
- return a.remainder_near(b, context=self)
-
- def same_quantum(self, a, b):
- """Returns True if the two operands have the same exponent.
-
- The result is never affected by either the sign or the coefficient of
- either operand.
-
- >>> ExtendedContext.same_quantum(Decimal('2.17'), Decimal('0.001'))
- False
- >>> ExtendedContext.same_quantum(Decimal('2.17'), Decimal('0.01'))
- True
- >>> ExtendedContext.same_quantum(Decimal('2.17'), Decimal('1'))
- False
- >>> ExtendedContext.same_quantum(Decimal('Inf'), Decimal('-Inf'))
- True
- """
- return a.same_quantum(b)
-
- def sqrt(self, a):
- """Returns the square root of a non-negative number to context precision.
-
- If the result must be inexact, it is rounded using the round-half-even
- algorithm.
-
- >>> ExtendedContext.sqrt(Decimal('0'))
- Decimal("0")
- >>> ExtendedContext.sqrt(Decimal('-0'))
- Decimal("-0")
- >>> ExtendedContext.sqrt(Decimal('0.39'))
- Decimal("0.624499800")
- >>> ExtendedContext.sqrt(Decimal('100'))
- Decimal("10")
- >>> ExtendedContext.sqrt(Decimal('1'))
- Decimal("1")
- >>> ExtendedContext.sqrt(Decimal('1.0'))
- Decimal("1.0")
- >>> ExtendedContext.sqrt(Decimal('1.00'))
- Decimal("1.0")
- >>> ExtendedContext.sqrt(Decimal('7'))
- Decimal("2.64575131")
- >>> ExtendedContext.sqrt(Decimal('10'))
- Decimal("3.16227766")
- >>> ExtendedContext.prec
- 9
- """
- return a.sqrt(context=self)
-
- def subtract(self, a, b):
- """Return the difference between the two operands.
-
- >>> ExtendedContext.subtract(Decimal('1.3'), Decimal('1.07'))
- Decimal("0.23")
- >>> ExtendedContext.subtract(Decimal('1.3'), Decimal('1.30'))
- Decimal("0.00")
- >>> ExtendedContext.subtract(Decimal('1.3'), Decimal('2.07'))
- Decimal("-0.77")
- """
- return a.__sub__(b, context=self)
-
- def to_eng_string(self, a):
- """Converts a number to a string, using scientific notation.
-
- The operation is not affected by the context.
- """
- return a.to_eng_string(context=self)
-
- def to_sci_string(self, a):
- """Converts a number to a string, using scientific notation.
-
- The operation is not affected by the context.
- """
- return a.__str__(context=self)
-
- def to_integral(self, a):
- """Rounds to an integer.
-
- When the operand has a negative exponent, the result is the same
- as using the quantize() operation using the given operand as the
- left-hand-operand, 1E+0 as the right-hand-operand, and the precision
- of the operand as the precision setting, except that no flags will
- be set. The rounding mode is taken from the context.
-
- >>> ExtendedContext.to_integral(Decimal('2.1'))
- Decimal("2")
- >>> ExtendedContext.to_integral(Decimal('100'))
- Decimal("100")
- >>> ExtendedContext.to_integral(Decimal('100.0'))
- Decimal("100")
- >>> ExtendedContext.to_integral(Decimal('101.5'))
- Decimal("102")
- >>> ExtendedContext.to_integral(Decimal('-101.5'))
- Decimal("-102")
- >>> ExtendedContext.to_integral(Decimal('10E+5'))
- Decimal("1.0E+6")
- >>> ExtendedContext.to_integral(Decimal('7.89E+77'))
- Decimal("7.89E+77")
- >>> ExtendedContext.to_integral(Decimal('-Inf'))
- Decimal("-Infinity")
- """
- return a.to_integral(context=self)
from _decimal import BasicContext, ExtendedContext, \
DefaultContext
@@ -2662,7 +1807,6 @@
__str__ = __repr__
-
def _normalize(op1, op2, shouldround = 0, prec = 0):
"""Normalizes op1, op2 to have the same exp and length of coefficient.
@@ -2783,125 +1927,3 @@
return (2, sign, num[4:].lstrip('0'))
return 0
-
-# ##### Setup Specific Contexts ################################
-
-# # The default context prototype used by Context()
-# # Is mutable, so that new contexts can have different default values
-
-# DefaultContext = Context(
-# prec=28, rounding=ROUND_HALF_EVEN,
-# traps=[DivisionByZero, Overflow, InvalidOperation],
-# flags=[],
-# _rounding_decision=ALWAYS_ROUND,
-# Emax=999999999,
-# Emin=-999999999,
-# capitals=1
-# )
-
-# # Pre-made alternate contexts offered by the specification
-# # Don't change these; the user should be able to select these
-# # contexts and be able to reproduce results from other implementations
-# # of the spec.
-
-# BasicContext = Context(
-# prec=9, rounding=ROUND_HALF_UP,
-# traps=[DivisionByZero, Overflow, InvalidOperation, Clamped, Underflow],
-# flags=[],
-# )
-
-# ExtendedContext = Context(
-# prec=9, rounding=ROUND_HALF_EVEN,
-# traps=[],
-# flags=[],
-# )
-
-
-# ##### Useful Constants (internal use only) ####################
-
-from _decimal import Inf, negInf, NaN
-
-# #Reusable defaults
-# Inf = Decimal('Inf')
-# negInf = Decimal('-Inf')
-
-# #Infsign[sign] is infinity w/ that sign
-Infsign = (Inf, negInf)
-
-# NaN = Decimal('NaN')
-
-
-
-
-# ##### crud for parsing strings #################################
-# import re
-
-# # There's an optional sign at the start, and an optional exponent
-# # at the end. The exponent has an optional sign and at least one
-# # digit. In between, must have either at least one digit followed
-# # by an optional fraction, or a decimal point followed by at least
-# # one digit. Yuck.
-
-# _parser = re.compile(r"""
-# # \s*
-# (?P<sign>[-+])?
-# (
-# (?P<int>\d+) (\. (?P<frac>\d*))?
-# |
-# \. (?P<onlyfrac>\d+)
-# )
-# ([eE](?P<exp>[-+]? \d+))?
-# # \s*
-# $
-# """, re.VERBOSE).match #Uncomment the \s* to allow leading or trailing spaces.
-
-# del re
-
-# # return sign, n, p s.t. float string value == -1**sign * n * 10**p exactly
-
-# def _string2exact(s):
-# m = _parser(s)
-# if m is None:
-# raise ValueError("invalid literal for Decimal: %r" % s)
-
-# if m.group('sign') == "-":
-# sign = 1
-# else:
-# sign = 0
-
-# exp = m.group('exp')
-# if exp is None:
-# exp = 0
-# else:
-# exp = int(exp)
-
-# intpart = m.group('int')
-# if intpart is None:
-# intpart = ""
-# fracpart = m.group('onlyfrac')
-# else:
-# fracpart = m.group('frac')
-# if fracpart is None:
-# fracpart = ""
-
-# exp -= len(fracpart)
-
-# mantissa = intpart + fracpart
-# tmp = map(int, mantissa)
-# backup = tmp
-# while tmp and tmp[0] == 0:
-# del tmp[0]
-
-# # It's a zero
-# if not tmp:
-# if backup:
-# return (sign, tuple(backup), exp)
-# return (sign, (0,), exp)
-# mantissa = tuple(tmp)
-
-# return (sign, mantissa, exp)
-
-
-if __name__ == '__main__':
- import doctest, sys
- doctest.testmod(sys.modules[__name__])
More information about the Python-checkins
mailing list