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

georg.brandl python-checkins at python.org
Tue May 23 16:06:03 CEST 2006


Author: georg.brandl
Date: Tue May 23 16:06:03 2006
New Revision: 46111

Modified:
   sandbox/trunk/decimal-c/_decimal.c
Log:
Finish rounding functions.



Modified: sandbox/trunk/decimal-c/_decimal.c
==============================================================================
--- sandbox/trunk/decimal-c/_decimal.c	(original)
+++ sandbox/trunk/decimal-c/_decimal.c	Tue May 23 16:06:03 2006
@@ -1,6 +1,8 @@
 /*  C implementation of the decimal module.
  *
  *  Partly written in Iceland by Georg Brandl.
+ *
+ *  vim:ts=4:sw=4:et:si
  */
 
 #include "Python.h"
@@ -254,10 +256,53 @@
     return 0;
 }
 
+/* add 1eExponent. This is more efficient than add, used in rounding. */
 static decimalobject *
-_decimal_increment(decimalobject *self, long prec, contextobject *ctx)
+_decimal_increment(decimalobject *self, int round, contextobject *ctx)
 {
-    /* XXX */
+    long spot;
+    decimalobject *new;
+
+    if (ISSPECIAL(self)) {
+        decimalobject *nan;
+        int res;
+        res = _check_nans(self, NULL, ctx, &nan);
+        if (res != 0) return nan;
+        
+        /* I'm infinite, so incrementing makes no difference. */
+        return decimal_copy(self);
+    }
+
+    new = _new_decimalobj(self->ob_size + 1,  /* we possibly need a new digit */
+                          self->sign, self->exp);
+    if (!new) return NULL;
+    
+    /* determine if we need a new digit */
+    for (spot = 0; spot < self->ob_size; spot++) {
+        if (self->digits[spot] != 9) {
+            spot = -1;
+            break;
+        }
+    }
+    if (spot != -1) {
+        /* no new digit needed */
+        new->ob_size--;
+        for (spot = 0; spot < self->ob_size; spot++)
+            new->digits[spot] = self->digits[spot];
+    } else {
+        for (spot = 0; spot < self->ob_size; spot++)
+            new->digits[spot+1] = self->digits[spot];
+    }
+
+    spot = new->ob_size-1;
+    new->digits[spot]++;
+    while (new->digits[spot] == 10) {
+        new->digits[spot] = 0;
+        spot--;
+        assert(spot >= 0);
+        new->digits[spot]++;
+    }
+    return new;
 }
 
 /* Round towards 0, that is, truncate digits. */
@@ -308,6 +353,7 @@
 {
     decimalobject *new;
     long i;
+    assert(expdiff > 0);
     if (!tmp) {
         tmp = _new_decimalobj(prec, self->sign, self->exp - expdiff);
         if (!tmp) return NULL;
@@ -334,6 +380,7 @@
 {
     long i;
     decimalobject *tmp;
+    assert(expdiff > 0);
     tmp = _new_decimalobj(prec, self->sign, self->exp - expdiff);
     if (!tmp) return NULL;
     for (i = 0; i < prec; i++)
@@ -352,6 +399,22 @@
 static decimalobject *
 _round_half_even(decimalobject *self, long prec, long expdiff, contextobject *ctx)
 {
+    decimalobject *tmp;
+    long i;
+    assert(expdiff > 0);
+    tmp = _new_decimalobj(prec, self->sign, self->exp - expdiff);
+    if (!tmp) return NULL;
+    for (i = 0; i < prec; i++)
+        tmp->digits[i] = self->digits[i];
+    if (self->digits[prec] == 5) {
+        for (i = prec+1; i < self->ob_size; i++) {
+            if (self->digits[i] != 0)
+                return _do_round_half_up(self, prec, expdiff, ctx, tmp);
+        }
+        if ((self->digits[prec-1] & 1) == 0)
+            return tmp;
+    }
+    return _do_round_half_up(self, prec, expdiff, ctx, tmp);
 }
 
 /* Round 5 up (away from 0). */
@@ -361,15 +424,26 @@
     return _do_round_half_up(self, prec, expdiff, ctx, NULL);
 }
 
+/* Round up (regardless of sign) */
 static decimalobject *
 _round_floor(decimalobject *self, long prec, long expdiff, contextobject *ctx)
 {
+    assert(self->sign <= 1);
+    if (self->sign > 0)
+        return _round_down(self, prec, expdiff, ctx);
+    else
+        return _round_up(self, prec, expdiff, ctx);
 }
 
+/* Round down (regardless of sign) */
 static decimalobject *
 _round_ceiling(decimalobject *self, long prec, long expdiff, contextobject *ctx)
 {
-    
+    assert(self->sign <= 1);
+    if (self->sign > 0)
+        return _round_up(self, prec, expdiff, ctx);
+    else
+        return _round_down(self, prec, expdiff, ctx);
 }
 
 typedef decimalobject*(*round_func)(decimalobject *, long, long, contextobject *);
@@ -997,7 +1071,11 @@
 static int
 decimal_nonzero(decimalobject *self)
 {
-    /* XXX */
+    long i;
+    if (ISSPECIAL(self))
+        return 1;
+    for (i = 0; i < self->ob_size; i++)
+        if (i != 0) return 1;
     return 0;
 }
 
@@ -1080,7 +1158,6 @@
 static decimalobject *
 _decimal_fromliteralnan(char *str, long len, contextobject *ctx)
 {
-    /* XXX: what if buffer is longer than LONG_MAX? */
     char literalsign = 0, sign = 0;
     decimalobject *new;
     long size = 0, i;
@@ -1170,11 +1247,11 @@
 {
     long ipos = 0;   /* start of integral digits */
     long dpos = -1;  /* decimal point location */
-    long dend = len; /* end of integral/decimal digits */
+    long dend = len; /* end of integral/decimal digits (last digit+1) */
     char *p = str;
     char sign = 0;
     decimalobject *new;
-    long exp = 0, i = 0, ndigits;
+    long exp = 0, i, ndigits;
 
     /* optional sign */
     if (*p == '+') {
@@ -1206,7 +1283,7 @@
         if (ipos == dpos && dpos == dend-1)
             /* no digits at all */
             goto err;
-        goto finish;
+        goto calculate;
     } else if (*p == '-' || *p == '+') {
         ++p;
     }
@@ -1217,20 +1294,31 @@
             goto err;
     } while (*++p);
 
-finish:
+calculate:
     
     if (dend < len)
-        /* XXX: which function to convert a string to ssize_t? */
         exp = atol(str + dend + 1);
     if (dpos >= 0)
         /* specified fractional digits, reduce exp */
         exp -= dend - dpos - 1;
 
+    /* If it's not a zero, strip leading 0s */
+    for (p = str+ipos; p-str < dend; ++p) {
+        if (*p != '0' && *p != '.') {
+            /* first nonzero digit */
+            ipos = (p-str);
+            goto finish;
+        }
+    }
+    /* it's a zero */
+    dend = (dpos == ipos ? ipos+2 : ipos+1);
+
+finish:
     ndigits = dend - ipos - (dpos<0 ? 0 : 1);
     new = _new_decimalobj(ndigits, sign, exp);
     if (!new)
         return NULL;
-    /* XXX: leading zeroes are not stripped */
+    i = 0;
     for (p = str+ipos; p-str < dend; ++p) {
         if (*p == '.') continue;
         new->digits[i] = *p - 48;
@@ -1574,7 +1662,7 @@
         if (val < 0 || val > 9) {
             PyObject_FREE(arr);
             PyErr_SetString(PyExc_TypeError, "_int digits must be 0-9");
-            return NULL;
+            return -1;
         }
         arr[i] = val;
     }


More information about the Python-checkins mailing list