[Python-checkins] r46091 - sandbox/trunk/decimal-c/_decimal.c

georg.brandl python-checkins at python.org
Tue May 23 11:53:13 CEST 2006


Author: georg.brandl
Date: Tue May 23 11:53:12 2006
New Revision: 46091

Modified:
   sandbox/trunk/decimal-c/_decimal.c
Log:
Update to current status.



Modified: sandbox/trunk/decimal-c/_decimal.c
==============================================================================
--- sandbox/trunk/decimal-c/_decimal.c	(original)
+++ sandbox/trunk/decimal-c/_decimal.c	Tue May 23 11:53:12 2006
@@ -6,6 +6,8 @@
 #include "structmember.h"
 #include "decimal.h"
 
+/* XXX: getcontext() perhaps shouldn't INCREF the ctx */
+
 /* helpful macros ************************************************************/
 
 #define MODULE_NAME   "_decimal"
@@ -244,12 +246,159 @@
     return 0;
 }
 
-/* default: rounding=-1 (use context), see the ROUND_* constants */
+static decimalobject *
+_round_down(decimalobject *self, long prec, long expdiff, contextobject *ctx)
+{
+}
+
+static decimalobject *
+_round_up(decimalobject *self, long prec, long expdiff, contextobject *ctx)
+{
+}
+
+static decimalobject *
+_round_half_down(decimalobject *self, long prec, long expdiff, contextobject *ctx)
+{
+}
+
+static decimalobject *
+_round_half_even(decimalobject *self, long prec, long expdiff, contextobject *ctx)
+{
+}
+
+static decimalobject *
+_round_half_up(decimalobject *self, long prec, long expdiff, contextobject *ctx)
+{
+}
+
+static decimalobject *
+_round_floor(decimalobject *self, long prec, long expdiff, contextobject *ctx)
+{
+}
+
+static decimalobject *
+_round_ceiling(decimalobject *self, long prec, long expdiff, contextobject *ctx)
+{
+}
+
+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 */
 static decimalobject *
 _decimal_round(decimalobject *self, long prec, contextobject *ctx, int rounding)
 {
-    /* XXX */
-    Py_RETURN_NONE;
+    decimalobject *new, *new2, *errres;
+    Py_ssize_t i, expdiff;
+    round_func rnd_func;
+    
+    if (ISSPECIAL(self)) {
+        decimalobject *nan = NULL;
+        int ret;
+        ret = _check_nans(self, NULL, ctx, &nan);
+        if (ret != 0) return nan;
+        if (ISINF(self)) return decimal_copy(self);
+    }
+    if (rounding == -1)
+        rounding = ctx->rounding;
+    if (prec == -1)
+        prec = ctx->prec;
+    
+    if (rounding < 0 || rounding > ROUND_CEILING) {
+        PyErr_SetString(PyExc_ValueError, "invalid rounding mode");
+        return NULL;
+    }
+
+    if (!decimal_nonzero(self)) {
+        if (prec <= 0)
+            i = 1;
+        else 
+            i = prec;
+        
+        new = _new_decimalobj(i, self->sign,
+                              self->ob_size - prec + self->exp);
+        if (!new) return NULL;
+        while (i--)
+            new->digits[i] = 0;
+        
+        errres = context_raise_error(ctx, S_ROUNDED, NULL, NULL);
+        if (!errres) {
+            Py_DECREF(new);
+            return NULL; /* error was set */
+        }
+        Py_DECREF(errres); /* we don't need the returned object */
+        return new;
+    }
+    
+    if (prec == 0) {
+        new = _new_decimalobj(self->ob_size+1, self->sign, self->exp);
+        if (!new) return NULL;
+        new->digits[0] = 0;
+        for (i = 1; i < new->ob_size; i++)
+            new->digits[i] = self->digits[i-1];
+        prec = 1;
+    } else if (prec < 0) {
+        new = _new_decimalobj(2, self->sign,
+                              self->exp + self->ob_size - prec - 1);
+        if (!new) return NULL;
+        new->digits[0] = 0;
+        new->digits[1] = 1;
+        prec = 1;
+    } else {
+        new = decimal_copy(self);
+        if (!new) return NULL;
+    }
+
+    expdiff = prec - new->ob_size;
+    if (expdiff == 0)
+        return new;
+    else if (expdiff > 0) {
+        /* we need to extend precision */
+        new2 = _new_decimalobj(prec, new->sign, new->exp - expdiff);
+        if (!new2) {
+            Py_DECREF(new);
+            return NULL;
+        }
+        for (i = 0; i < new->ob_size; i++) {
+            new2->digits[i] = new->digits[i];
+        }
+        for (i = new->ob_size; i < new2->ob_size; i++) {
+            new2->digits[i] = 0;
+        }
+        Py_DECREF(new);
+        return new2;
+    }
+
+    /* Maybe all the lost digits are 0. */
+    for (i = expdiff; i < self->ob_size; i++) {
+        if (self->digits[i] > 0)
+            goto no_way;
+    }
+    /* All lost digits are 0. */
+    new2 = _new_decimalobj(prec, new->sign, new->exp - expdiff);
+    if (!new2) {
+        Py_DECREF(new);
+        return NULL;
+    }
+    for (i = 0; i < new2->ob_size; i++)
+        new2->digits[i] = new->digits[i];
+    Py_DECREF(new);
+    errres = context_raise_error(ctx, S_ROUNDED, NULL, NULL);
+    if (!errres) {
+        Py_DECREF(new2);
+        return NULL;
+    }
+    Py_DECREF(errres);
+    return new2;
+
+no_way:
+    /* Now rounding starts. */
+    rnd_func = round_funcs[rounding];
+
+
 }
 
 /* Default values: rounding=-1 (use context), watchexp=1 */
@@ -257,9 +406,9 @@
 _decimal_rescale(decimalobject *self, long exp, contextobject *ctx,
                  int rounding, int watchexp)
 {
-    decimalobject *ans = NULL;
+    decimalobject *ans = NULL, *tmp, *tmp2;
     int ret;
-    long diff;
+    long diff, adj;
     Py_ssize_t digits, i;
 
     if (ISSPECIAL(self)) {
@@ -276,7 +425,7 @@
 
     if (!decimal_nonzero(self)) {
         ans = _new_decimalobj(1, self->sign, exp);
-	if (!ans)
+        if (!ans)
             return NULL;
         ans->digits[0] = 0;
         return ans;
@@ -289,20 +438,54 @@
         return context_raise_error(ctx, S_INV_OPERATION, "Rescale > prec", NULL);
 
     digits += 1;
-    ans = _new_decimalobj(self->ob_size+1, self->sign, self->exp);
-    if (!ans)
-        return NULL;
-    for (i = 0; i < self->ob_size; i++)
-        ans->digits[i+1] = self->digits[i];
-    ans->digits[0] = 0;
-
     if (digits < 0) {
-        ans->exp = -digits + ans->exp;
-    /* XXX: not complete... */
+        ans = _new_decimalobj(2, self->sign, self->exp - digits);
+        if (!ans)
+            return NULL;
+        ans->digits[0] = 0;
+        ans->digits[1] = 1;
+        digits = 1;
+    } else {
+        ans = _new_decimalobj(self->ob_size+1, self->sign, self->exp);
+        if (!ans)
+            return NULL;
+        for (i = 0; i < self->ob_size; i++)
+            ans->digits[i+1] = self->digits[i];
+        ans->digits[0] = 0;
     }
 
-        
+    tmp = _decimal_round(ans, digits, ctx, rounding);
+    Py_DECREF(ans);
+    if (!tmp)
+        return NULL;
 
+    if (tmp->digits[0] == 0 && tmp->ob_size > 1) {
+        tmp2 = _new_decimalobj(tmp->ob_size-1, tmp->sign, tmp->exp);
+        if (!tmp2) {
+            Py_DECREF(tmp);
+            return NULL;
+        }
+        for (i = 1; i < tmp->ob_size; i++)
+            tmp2->digits[i] = tmp->digits[i];
+        Py_DECREF(tmp);
+        tmp = tmp2;
+    }
+    tmp->exp = exp;
+
+    adj = ADJUSTED(tmp);
+    if (decimal_nonzero(tmp)) {
+        if (adj < ctx->Emin) {
+            ans = context_raise_error(ctx, S_SUBNORMAL, NULL, NULL);
+            Py_DECREF(tmp);
+            return ans;
+        } else if (adj > ctx->Emax) {
+            ans = context_raise_error(ctx, S_INV_OPERATION,
+                                      "rescale(a, INF)", NULL);
+            Py_DECREF(tmp);
+            return ans;
+        }
+    }
+    return tmp;
 }
 
 /* Fix the exponents and return a copy with the exponents in bounds.
@@ -443,35 +626,142 @@
      { return NULL; } \
   if (ctx == NULL) { \
       if (!(ctx = getcontext())) return NULL; \
+  } else if (!PyDecimalContext_Check(ctx)) { \
+      PyErr_SetString(PyExc_TypeError, "context must be a Context object"); \
+      return NULL; \
   }
 
 STUB(compare)
 STUB(max)
 STUB(min)
 
-static PyObject *
+static decimalobject *
 decimal_normalize(decimalobject *self, PyObject *args, PyObject *kwds)
 {
-    decimalobject *dup;
+    decimalobject *dup, *new;
     contextobject *ctx = NULL;
+    Py_ssize_t end, i;
+    long exp;
     PARSECONTEXT("normalize");
 
     if (ISSPECIAL(self)) {
         decimalobject *nan = NULL;
         int res;
         res = _check_nans(self, NULL, ctx, &nan);
-        /* if error set or NaN returned, return */
-        if (res != 0) return (PyObject *)nan;
+        if (res != 0) return nan;  /* can be NULL on error */
     }
 
     dup = _decimal_fix(self, ctx);
+    if (ISINF(dup))
+        return dup;
+
+    if (!decimal_nonzero(dup)) {
+        new = _new_decimalobj(1, dup->sign, 0);
+        Py_DECREF(dup);
+        if (!new) return NULL;
+        new->digits[0] = 0;
+        return new;
+    }
+
+    end = dup->ob_size;
+    exp = dup->exp;
+    while (dup->digits[end-1] == 0) {
+        end--;
+        exp++;
+    }
+    new = _new_decimalobj(end, dup->sign, exp);
+    if (!new) {
+        Py_DECREF(dup);
+        return NULL;
+    }
+    for (i = 0; i < end; i++)
+        new->digits[i] = dup->digits[i];
+    Py_DECREF(dup);
+    return new;
+    
+}
 
-    /* XXX: incomplete */
+static decimalobject *
+decimal_quantize(decimalobject *self, PyObject *args, PyObject *kwds)
+{
+    static char *kwlist[] = {"exp", "rounding", "context", "watchexp", 0};
+    contextobject *ctx = NULL;
+    decimalobject *other = NULL;
+    int rounding = -1, watchexp = 1;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iOi:quantize", kwlist,
+                                     &other, &rounding, &ctx, &watchexp))
+        return NULL;
+
+    if (ctx == NULL) {
+        if (!(ctx = getcontext()))
+            return NULL;
+    } else if (!PyDecimalContext_Check(ctx)) {
+        PyErr_SetString(PyExc_TypeError, "context must be a Context object");
+        return NULL;
+    }
+    if (!PyDecimal_Check(other)) {
+        PyErr_SetString(PyExc_TypeError, "exp must be a Decimal object");
+        return NULL;
+    }
+    if (ISSPECIAL(self) || ISSPECIAL(other)) {
+        decimalobject *nan = NULL;
+        int res;
+        res = _check_nans(self, other, ctx, &nan);
+        if (res != 0) return nan;  /* can be NULL on error */
+
+        if (ISINF(self) || ISINF(other)) {
+            if (ISINF(self) && ISINF(other)) {
+                Py_INCREF(self);
+                return self;
+            }
+            return context_raise_error(ctx, S_INV_OPERATION,
+                                       "quantize with one INF", NULL);
+        }
+    }
+    return _decimal_rescale(self, other->exp, ctx, rounding, watchexp);
 }
 
-STUB(quantize)
+
 STUB(remainder_near)
-STUB(same_quantum)
+
+static PyObject *
+decimal_same_quantum(decimalobject *self, PyObject *args, PyObject *kwds)
+{
+    static char *kwlist[] = {"other", 0};
+    decimalobject *other = NULL;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:same_quantum", kwlist,
+                                     &other))
+        return NULL;
+
+    if (!PyDecimal_Check(other)) {
+        PyErr_SetString(PyExc_TypeError, "other must be a Decimal object");
+        return NULL;
+    }
+
+    if (ISSPECIAL(self) && ISSPECIAL(other)) {
+        if (GETNAN(self) || GETNAN(other)) {
+            if (GETNAN(self) && GETNAN(other))
+                Py_RETURN_TRUE;
+            else
+                Py_RETURN_FALSE;
+        }
+        if (ISINF(self) || ISINF(other)) {
+            if (ISINF(self) && ISINF(other))
+                Py_RETURN_TRUE;
+            else
+                Py_RETURN_FALSE;
+        }
+    }
+
+    if (self->exp == other->exp)
+        Py_RETURN_TRUE;
+    else
+        Py_RETURN_FALSE;
+}
+
+
 STUB(sqrt)
 STUB(to_eng_string)
 STUB(to_integral)
@@ -695,7 +985,7 @@
     char literalsign = 0, sign = 0;
     decimalobject *new;
     Py_ssize_t size = 0, i;
-    char *start, *p;
+    char *start = NULL, *p;
 
     if (len < 3)
         return NULL;
@@ -908,7 +1198,7 @@
 PyObject *
 PyDecimal_FromSequence(PyObject *seq)
 {
-    decimalobject *new;
+    decimalobject *new = NULL;
     PyObject *tup, *digits, *digtup = NULL, *item;
     int sign;
     long exp;
@@ -1123,7 +1413,7 @@
     tmp2 = PyTuple_New(0);
     if (!tmp2)
         return -1;
-    tmp = decimal_normalize(d, tmp2, NULL);
+    tmp = (PyObject *)decimal_normalize(d, tmp2, NULL);
     Py_DECREF(tmp2);
     if (!tmp)
         return -1;


More information about the Python-checkins mailing list