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

mateusz.rukowicz python-checkins at python.org
Sun Oct 14 23:07:47 CEST 2007


Author: mateusz.rukowicz
Date: Sun Oct 14 23:07:47 2007
New Revision: 58462

Modified:
   sandbox/trunk/decimal-c/_decimal.c
Log:
Added 05up rounding. Fixed some issues with NaNs and precision (now NaNs diagnostics are truncated to context precision digits).


Modified: sandbox/trunk/decimal-c/_decimal.c
==============================================================================
--- sandbox/trunk/decimal-c/_decimal.c	(original)
+++ sandbox/trunk/decimal-c/_decimal.c	Sun Oct 14 23:07:47 2007
@@ -1157,7 +1157,8 @@
 #define ROUND_HALF_UP   4
 #define ROUND_FLOOR     5
 #define ROUND_CEILING   6
-#define VALID_ROUND(x) ((x) <= ROUND_CEILING && (x) >= ROUND_DOWN)
+#define ROUND_05UP      7
+#define VALID_ROUND(x) ((x) <= ROUND_05UP && (x) >= ROUND_DOWN)
 
 /* for context->rounding_dec */
 #define ALWAYS_ROUND    0
@@ -1277,6 +1278,7 @@
 static decimalobject *_do_decimal_subtract(decimalobject *, decimalobject *, contextobject *);
 static PyObject *context_ignore_flags(contextobject *self, PyObject *args);
 static decimalobject *_do_decimal_power(decimalobject *, decimalobject *, decimalobject *, contextobject *);
+static decimalobject *_decimal_fix_nan(decimalobject *self, contextobject *ctx);
 static int _decimal_isint(decimalobject*);
 
 /* Exception handlers *********************************************************/
@@ -1306,7 +1308,7 @@
 handle_InvalidOperation(PyTypeObject *type, contextobject *ctx,
                         char *expl, decimalobject *thing)
 {
-    decimalobject *res;
+    decimalobject *res, *res2;
     long sign, i;
     HANDLE_ERROR(ctx, S_INV_OPERATION, expl, NULL);
 
@@ -1320,13 +1322,16 @@
         sign = SIGN_NEGNAN;
     else
         sign = SIGN_POSNAN;
-
+    /* TODO actually, we don't need to call fixnan, but we do in case it will change */
     res = _new_decimalobj(type, thing->ob_size, sign, exp_from_i(0));
     if (!res) return NULL;
     
     for (i = 0; i< res->limb_count;i++)
         res->limbs[i] = thing->limbs[i];
-    return res;
+    res2 = _decimal_fix_nan(res, ctx);
+    if(!res2) return NULL;
+    Py_DECREF(res);
+    return res2;
 }
 
 static decimalobject *
@@ -1545,14 +1550,11 @@
                 return -1;
             return 1;
         }
-        
+        /* decimal_fix gives us new reference, no INCREF requiered */  
         if (nan1)
-            *res = x;
+            *res = _decimal_fix_nan(x, ctx);
         else
-            *res = y;
-        /* since handle_InvalidOperation above returns new references, to be
-         * consistent we must incref the returns here too. */
-        Py_INCREF(*res);
+            *res = _decimal_fix_nan(y, ctx);
         return 1;
     }
     return 0;
@@ -1743,12 +1745,21 @@
         return _round_down(self, prec, expdiff, ctx);
 }
 
+/* Round up if digit prec-1 (0 based) is 0 or 5, otherwise round down*/
+static decimalobject *
+_round_05up(decimalobject *self, long prec, exp_t expdiff, contextobject *ctx) {
+    long dig = _limb_get_digit(self->limbs, self->ob_size, prec-1);
+    if(dig == 0 || dig == 5) 
+        return _round_up(self, prec, expdiff, ctx);
+    else 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, exp_t, contextobject *);
 static round_func round_funcs[] = {
     _round_down, _round_up, _round_half_down, _round_half_even,
-    _round_half_up, _round_floor, _round_ceiling
+    _round_half_up, _round_floor, _round_ceiling, _round_05up
 };
 
 /* default: prec=-1, rounding=-1 (use context values), see the ROUND_* constants */
@@ -2116,12 +2127,35 @@
     return NULL;
 }
 
+
+static decimalobject *
+_decimal_fix_nan(decimalobject *self, contextobject *ctx) {
+    long max_diagnostic_len = ctx->prec - ctx->clamp;
+    long limbs = (max_diagnostic_len + LOG - 1) / LOG;
+    int i;
+    if(self->ob_size <= max_diagnostic_len) {
+        Py_INCREF(self);
+        return self;
+    }
+    /* we need to copy first ceil(self->ob_size / LOG) limbs, and set size
+     * because we truncate most significant limbs */
+    decimalobject *ans = _new_decimalobj(self->ob_type, max_diagnostic_len, self->sign, self->exp);
+    if(!ans)
+        return NULL;
+    for(i = 0; i < limbs; i ++) {
+        ans->limbs[i] = self->limbs[i];
+    }
+    return ans;
+}
+
 static decimalobject *
 _decimal_fix(decimalobject *self, contextobject *ctx)
 {
     decimalobject *ans = NULL;
 
     if (ISSPECIAL(self)) {
+        if(GETNAN(self)) 
+            return _decimal_fix_nan(self, ctx);
         Py_INCREF(self);
         return self;
     }
@@ -2144,6 +2178,8 @@
     return ans;
 }
 
+//this one cuts off diagnostic information
+
 static PyObject *
 decimal_fix(decimalobject *self, PyObject *args, PyObject *kwds)
 {
@@ -6633,8 +6669,9 @@
     return NULL;
 
 finish:
-    if (size > ctx->prec)
-        return handle_ConversionSyntax(type, ctx, "diagnostic info too long");
+    /* not used anymore */
+/*    if (size > ctx->prec)
+        return handle_ConversionSyntax(type, ctx, "diagnostic info too long"); */
 
     new = _new_decimalobj(type, size, sign, exp_from_i(0));
     if (!new)
@@ -8297,6 +8334,9 @@
         case ROUND_CEILING:
             strcat(roundstr, "CEILING");
             break;
+        case ROUND_05UP:
+            strcat(roundstr, "05UP");
+            break;
         default:
             strcpy(roundstr, "None");
     }
@@ -8553,19 +8593,21 @@
     if (PyString_Check(value)) {
         char *buffer = PyString_AS_STRING(value);
         if (!strcmp("ROUND_DOWN", buffer))
-            new_round = 0;
+            new_round = ROUND_DOWN;
         else if (!strcmp("ROUND_UP", buffer))
-            new_round = 1;
+            new_round = ROUND_UP;
         else if (!strcmp("ROUND_HALF_DOWN", buffer))
-            new_round = 2;
+            new_round = ROUND_HALF_DOWN;
         else if (!strcmp("ROUND_HALF_EVEN", buffer))
-            new_round = 3;
+            new_round = ROUND_HALF_EVEN;
         else if (!strcmp("ROUND_HALF_UP", buffer))
-            new_round = 4;
+            new_round = ROUND_HALF_UP;
         else if (!strcmp("ROUND_FLOOR", buffer))
-            new_round = 5;
+            new_round = ROUND_FLOOR;
         else if (!strcmp("ROUND_CEILING", buffer))
-            new_round = 6;
+            new_round = ROUND_CEILING;
+        else if (!strcmp("ROUND_05UP", buffer))
+            new_round = ROUND_05UP;
     }
     else if (new_round == -1) {
         PyErr_SetString(PyExc_TypeError, "Rounding should be int or string");
@@ -8813,6 +8855,7 @@
     ADD_CONST(m, ROUND_HALF_UP);
     ADD_CONST(m, ROUND_FLOOR);
     ADD_CONST(m, ROUND_CEILING);
+    ADD_CONST(m, ROUND_05UP);
     ADD_CONST(m, ALWAYS_ROUND);
     ADD_CONST(m, NEVER_ROUND);
     


More information about the Python-checkins mailing list