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

mateusz.rukowicz python-checkins at python.org
Sat Jun 10 19:56:57 CEST 2006


Author: mateusz.rukowicz
Date: Sat Jun 10 19:56:54 2006
New Revision: 46823

Modified:
   sandbox/trunk/decimal-c/_decimal.c
Log:
Huge improvement, *many* bugs fixed, adding and substracting now works. Improved Contexts, roundings, and almost every function.


Modified: sandbox/trunk/decimal-c/_decimal.c
==============================================================================
--- sandbox/trunk/decimal-c/_decimal.c	(original)
+++ sandbox/trunk/decimal-c/_decimal.c	Sat Jun 10 19:56:54 2006
@@ -171,7 +171,30 @@
 static void
 _limb_fill(long *self, long ndigits, long x)
 {
-	//TODO
+	long full_limb = 0;
+	long mult = 1;
+	long i;
+	while(mult != BASE)
+	{
+		full_limb += x * mult;
+		mult *= 10;
+	}
+
+	long limbs = ndigits / LOG;
+
+	for(i = 0;i<limbs;i++)
+		self[i] = full_limb;
+
+	mult = 1;
+	ndigits -= limbs * LOG;
+	if(ndigits)
+		self[limbs] = 0;
+	
+	while(ndigits--)
+	{
+		self[limbs] += mult * x;
+		mult *= 10;
+	}
 }
 
 static long
@@ -189,6 +212,97 @@
 	return tmp%10;
 }
 
+static int
+_limb_compare(long *self, long limbs, long *other, long olimbs)
+{
+	long i;
+	if(limbs != olimbs)
+		return limbs > olimbs ? 1 : -1;
+
+	for(i = limbs-1; i>=0 ;i--)
+		if(self[i] != other[i])
+			return self[i] > other[i] ? 1 : -1;
+	return 0;
+}
+
+static long
+_limb_add_core(long *big, long bsize, long *small, long ssize, long *out)
+{
+	long max = bsize + 1;
+	long limbs_total = (max + LOG -1)/LOG;
+	long limbs_copy = (bsize + LOG -1)/LOG;
+	long limbs_add = (ssize + LOG -1)/LOG;
+	long i;
+
+	for(i =0 ;i<limbs_add; i++)
+		out[i] = big[i] + small[i];
+
+	for(i = limbs_add;i<limbs_copy;i++)
+		out[i] = big[i];
+
+	for(i = limbs_copy;i<limbs_total;i++)
+		out[i] = 0;
+
+	for(i=0; i< limbs_total;i++)
+	{
+		if(out[i] >= BASE)
+		{
+			out[i] -= BASE;
+			out[i+1] ++;
+		}
+	}
+
+	/* there may be some extra digit at max, check this out */
+	if(_limb_get_digit(out, max, 0) > 0)
+		return max;
+	else
+		return max-1;
+		
+}
+
+
+static long
+_limb_add(long *self, long ssize, long *other, long osize, long *out)
+{
+	if(ssize > osize)
+		return _limb_add_core(self, ssize, other, osize, out);
+	else
+		return _limb_add_core(other, osize, self, ssize, out);
+}
+
+static long
+_limb_sub(long *big, long bsize, long *small, long ssize, long *out)
+{
+	long blimbs = (bsize + LOG -1)/LOG;
+	long slimbs = (ssize + LOG -1)/LOG;
+	long i;
+
+	for(i=0;i<blimbs;i++)
+		out[i] = big[i];
+
+	for(i=0;i<slimbs;i++)
+		out[i] -= small[i];
+
+	for(i=0;i<blimbs;i++)
+	{
+		if(out[i] < 0)
+		{
+				out[i] += BASE;
+				out[i+1] --;
+		}
+	}
+
+	long left_limbs = blimbs-1;
+
+	while(!out[left_limbs]) left_limbs --;
+	long left_digits = (left_limbs+1) * LOG;
+	
+	while(_limb_get_digit(out, left_digits, 0) == 0) left_digits --;
+	
+	return left_digits;
+}
+
+
 /* helpful macros ************************************************************/
 
 /* if we later decide to call this module "squeaky" */
@@ -391,8 +505,13 @@
     }
     assert(PyDecimal_Check(thing));
     /* we want to return a NaN, but keep diagnostics */
-    sign = (thing->sign == 0 ? SIGN_POSNAN : SIGN_NEGNAN);
-    res = _new_decimalobj(type, thing->ob_size, sign, 0);
+//    sign = ((thing->sign&1 == 0) ? SIGN_POSNAN : SIGN_NEGNAN);
+	if(thing->sign&1)		/* neg */
+		sign = SIGN_NEGNAN;
+	else
+		sign = SIGN_POSNAN;
+
+	res = _new_decimalobj(type, thing->ob_size, sign, 0);
     if (!res) return NULL;
     for (i = 0; i < thing->ob_size; i++)
         res->digits[i] = thing->digits[i];	/* DELETE */
@@ -657,31 +776,31 @@
     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) {
+//    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];
-		new->digits[0] = 0;
-    }
+//        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];
+//		new->digits[0] = 0;
+//    }
 
-    spot = new->ob_size-1;
-    new->digits[spot]++;
-    while (new->digits[spot] == 10) {
-        new->digits[spot] = 0;
-        spot--;
-        assert(spot >= 0);
-        new->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]++;
+//    }
 
 	for(i=0;i<self->limb_count;i++)
 			new->limbs[i] = self->limbs[i];
@@ -697,6 +816,8 @@
 			new->limbs[i+1] ++;
 			i++;
 	}
+	if(_limb_get_digit(new->limbs, new->ob_size, 0) == 0)
+		new->ob_size --;
     return new;
 }
 
@@ -705,7 +826,7 @@
 _round_down(decimalobject *self, long prec, long expdiff, contextobject *ctx)
 {
     long i;
-    decimalobject *new = _NEW_decimalobj(prec, self->sign, self->exp - expdiff);
+    decimalobject *new = _NEW_decimalobj(prec, self->sign, self->exp + expdiff);
     if (!new) return NULL;
     for (i = 0; i < prec; i++)
         new->digits[i] = self->digits[i];
@@ -719,7 +840,7 @@
 _round_up(decimalobject *self, long prec, long expdiff, contextobject *ctx)			
 {			/* XXX temporary solution with limbs */
     long i;
-    decimalobject *new = _NEW_decimalobj(prec, self->sign, self->exp - expdiff);
+    decimalobject *new = _NEW_decimalobj(prec, self->sign, self->exp + expdiff);
     decimalobject *new2 = NULL;
     if (!new) return NULL;
     for (i = 0; i < prec; i++)
@@ -783,7 +904,7 @@
         tmp->digits[i] = self->digits[i];
 
 	last = _limb_first_n_digits(self->limbs, self->ob_size, 0, tmp->limbs, prec);
-	assert(self->digits[prec] == last);
+//	assert(self->digits[prec] == last);
     if (last == 5) {
         for (i = prec+1; i < self->ob_size; i++) {
 			if(_limb_get_digit(self->limbs, self->ob_size, i) != 0)	/* SLOW */
@@ -808,8 +929,6 @@
     for (i = 0; i < prec; i++)
         tmp->digits[i] = self->digits[i];
 	last = _limb_first_n_digits(self->limbs, self->ob_size, 0, tmp->limbs, prec);
-	last = self->digits[prec];
-	assert(last == self->digits[prec]);
     if (last == 5) {
         for (i = prec+1; i < self->ob_size; i++) {
 			if(_limb_get_digit(self->limbs, self->ob_size, i) != 0)	/* SLOW */
@@ -817,7 +936,7 @@
                 return _do_round_half_up(self, prec, expdiff, ctx, tmp);
         }
 //        if ((self->digits[prec-1] & 1) == 0)
-		if((_limb_get_digit(self->limbs, self->ob_size, i)&1) == 0)
+		if((_limb_get_digit(self->limbs, self->ob_size, prec-1)&1) == 0)
 			return tmp;
     }
     return _do_round_half_up(self, prec, expdiff, ctx, tmp);
@@ -839,7 +958,7 @@
 
 /* Round up (regardless of sign) */
 static decimalobject *
-_round_floor(decimalobject *self, long prec, long expdiff, contextobject *ctx)
+_round_ceiling(decimalobject *self, long prec, long expdiff, contextobject *ctx)
 {
     assert(self->sign <= 1);
     if (self->sign > 0)
@@ -850,7 +969,7 @@
 
 /* Round down (regardless of sign) */
 static decimalobject *
-_round_ceiling(decimalobject *self, long prec, long expdiff, contextobject *ctx)
+_round_floor(decimalobject *self, long prec, long expdiff, contextobject *ctx)
 {
     assert(self->sign <= 1);
     if (self->sign > 0)
@@ -957,14 +1076,19 @@
     }
 
     /* Maybe all the lost digits are 0. */
-    for (i = expdiff; i < self->ob_size; i++) {
+    for (i = self->ob_size - expdiff; i < self->ob_size; i++) {
 //        if (self->digits[i] > 0)
 		if(_limb_get_digit(self->limbs, self->ob_size, i) > 0)
 			goto no_way;
     }
     /* All lost digits are 0, so just clobber new */
-    new->ob_size = prec;
-    new->exp += expdiff;
+//    new->ob_size = prec;
+	while(new->ob_size > prec)
+	{
+		_limb_cut_one_digit(new->limbs, new->ob_size);	/* VERY SLOW */
+		new->ob_size --;
+	}
+	new->exp += expdiff;
     if (handle_Rounded(ctx, NULL) != 0) {
         Py_DECREF(new);
         return NULL;
@@ -974,15 +1098,18 @@
 no_way:
     /* Now rounding starts. We still own "new". */
     rnd_func = round_funcs[rounding];
-    if (prec != ctx->prec) {
-        ctx2 = (contextobject *)context_copy(ctx);
-        if (!ctx2) {
-            Py_DECREF(new);
-            return NULL;
-        }
-        ctx2->prec = prec;
-        ctx = ctx2;
-    }
+//    if (prec != ctx->prec) {
+//        ctx2 = (contextobject *)context_copy(ctx);
+//        if (!ctx2) {
+//            Py_DECREF(new);
+//            return NULL;
+//        }
+//        ctx2->prec = prec;
+//        ctx = ctx2;
+  /*  }*/	/* XXX here is quite subtle bug - we copy context, and set flags in copied context
+		   after that, they are lost, not sure if we really need function above,
+		   I'll comment it */
+	
     /* ctx2 is NULL if the original context is used, because that one
      * doesn't have to be DECREF'd. */
     new2 = rnd_func(new, prec, expdiff, ctx);
@@ -1002,7 +1129,7 @@
     return NULL;
 }
 
-/* Default values: rounding=-1 (use context), watchexp=1 */
+/* Default values: rounding=-1 (use context), watchexp=1 */			/* TODO TODO TODO */
 static decimalobject *
 _decimal_rescale(decimalobject *self, long exp, contextobject *ctx,
                  int rounding, int watchexp)
@@ -1058,7 +1185,9 @@
         for (i = 0; i < self->ob_size; i++)
             ans->digits[i+1] = self->digits[i];
         ans->digits[0] = 0;
-		_limb_first_n_digits(self->limbs, self->ob_size, 0, ans->limbs, ans->ob_size);
+		for(i=0;i<ans->limb_count;i++)
+			ans->limbs[i] = 0;
+		_limb_first_n_digits(self->limbs, self->ob_size, 0, ans->limbs, ans->ob_size-1);
     }
 
     tmp = _decimal_round(ans, digits, ctx, rounding);
@@ -1066,11 +1195,12 @@
     if (!tmp)
         return NULL;
 
-    if (tmp->digits[0] == 0 && tmp->ob_size > 1) {
-        /* We need one digit less, just clobber tmp. */
+//    if (tmp->digits[0] == 0 && tmp->ob_size > 1) {
+	if(_limb_get_digit(tmp -> limbs, tmp->ob_size, 0) == 0 && tmp->ob_size > 1){
+	/* We need one digit less, just clobber tmp. */
         for (i = 0; i < tmp->ob_size-1; i++)
             tmp->digits[i] = tmp->digits[i+1];
-		_limb_cut_one_digit(tmp->limbs, tmp->ob_size);
+//		_limb_cut_one_digit(tmp->limbs, tmp->ob_size);
 		tmp->ob_size--;
     }
     tmp->exp = exp;
@@ -1809,12 +1939,19 @@
         SANITY_CHECK(p);
 
         /* check for digits != {0, } */
-        if (d->ob_size != 1 || d->digits[0] != 0) {
-            for (i = 0; i < d->ob_size; i++) {
-                p += sprintf(p, "%d", d->digits[i]);
-                SANITY_CHECK(p);
-            }
-        }
+//        if (d->ob_size != 1 || d->digits[0] != 0) {
+//            for (i = 0; i < d->ob_size; i++) {
+//                p += sprintf(p, "%d", d->digits[i]);
+//                SANITY_CHECK(p);
+//            }
+		if(d->ob_size != 1 || _limb_get_digit(d->limbs, d->ob_size, 0) != 0)
+		{
+			for(i=0;i< d->ob_size;i++)
+			{
+				p+= sprintf(p, "%d", _limb_get_digit(d->limbs, d->ob_size, i));	/* SLOW */
+				SANITY_CHECK(p);
+			}
+		}
     } else { /* infinity */
         p += sprintf(p, "%s", "Infinity");
     }
@@ -2213,7 +2350,9 @@
 {
     int shouldround, sign, negativezero = 0;
     long exp, oexp;
-    decimalobject *res, *res2;
+	long prec,cmp;
+    decimalobject *res, *res2, *ret, *o1, *o2;
+	prec = ctx->prec;
     
     if (ISSPECIAL(self) || ISSPECIAL(other)) {
         decimalobject *nan;
@@ -2249,12 +2388,13 @@
         res = _new_decimalobj(self->ob_type, 1, sign, exp);
         if (!res) return NULL;
         res->digits[0] = 0;
+		res->limbs[0] = 0;
         return res;
     }
     if (!decimal_nonzero(self)) {
         oexp = other->exp - ctx->prec - 1;
         exp = (exp > oexp ? exp : oexp);
-        res = _decimal_rescale(other, exp, ctx, 0, 1);
+        res = _decimal_rescale(other, exp, ctx, 0, 0);
         if (!res) return NULL;
         if (shouldround) {
             res2 = _decimal_fix(res, ctx);
@@ -2267,7 +2407,7 @@
     if (!decimal_nonzero(other)) {
         oexp = self->exp - ctx->prec - 1;
         exp = (exp > oexp ? exp : oexp);
-        res = _decimal_rescale(self, exp, ctx, 0, 1);
+        res = _decimal_rescale(self, exp, ctx, 0, 0);
         if (!res) return NULL;
         if (shouldround) {
             res2 = _decimal_fix(res, ctx);
@@ -2278,8 +2418,161 @@
         }            
     }
 
-    /* XXX */
-    Py_RETURN_NONE;
+	decimalobject *tmp;	/* we borrow refference */
+	decimalobject *oother;
+
+	long numdigits = self->exp - other->exp;
+
+	if(numdigits < 0)
+	{
+		numdigits *= -1;
+		tmp = other;
+		oother = self;
+	}
+	else
+	{
+		tmp = self;
+		oother = other;
+	}
+	
+	/* we borrow refference */
+	if(shouldround && numdigits > prec + 1)
+	{
+		if(numdigits > (oother->ob_size + prec + 1 - tmp->ob_size))
+		{
+			long extend = prec + 2 - tmp->ob_size;
+			if(extend <= 0)
+				extend = 1;
+			o1 = _NEW_decimalobj(tmp->ob_size + extend, tmp->sign, tmp->exp - extend);
+			if(!o1)
+				return NULL;
+
+			_limb_first_n_digits(tmp->limbs, tmp->ob_size, 0, o1->limbs, o1->ob_size);
+			o2 = _NEW_decimalobj(1, oother->sign, o1->exp);
+			if(!o2)
+			{
+				Py_XDECREF(o1);
+				return NULL;
+			}
+			o2->limbs[0] =1;
+			goto calc;
+		}	
+	}
+
+	o1 = _NEW_decimalobj(tmp->ob_size + numdigits, tmp->sign, tmp->exp - numdigits);
+	_limb_first_n_digits(tmp->limbs, tmp->ob_size, 0, o1->limbs, o1->ob_size);
+
+	if(!o1)
+	{
+		return NULL;
+	}
+	o2 = oother;
+	Py_INCREF(o2);
+
+
+calc:
+
+	assert(o1->exp == o2->exp);
+	exp = o1->exp;
+	cmp = _limb_compare(o1->limbs, o1->limb_count, o2->limbs, o2->limb_count);
+
+	if(o1->sign != o2->sign)
+	{
+		if(!cmp)
+		{
+			Py_DECREF(o1);
+			Py_DECREF(o2);
+			ret = _NEW_decimalobj(1, negativezero, exp);
+			if(!ret)
+				return NULL;
+			ret->limbs[0] = 0;
+			if(ret->exp < ETINY(ctx))
+			{
+				ret->exp = ETINY(ctx);
+				if(handle_Clamped(ctx, NULL) != 0)
+				{
+					Py_DECREF(ret);
+					return NULL;	
+				}
+			}
+			
+			return ret;
+		}
+		else
+		{
+			long max_size;
+			if(cmp == -1)
+			{
+				ret = o1;
+				o1 = o2;
+				o2 = ret;
+				ret = 0;
+			}
+			/* o1 > o2 */
+			max_size = o1->ob_size > o2->ob_size ? o1->ob_size : o2->ob_size;
+			ret = _NEW_decimalobj(max_size, o1->sign, o1->exp);
+			if(!ret)
+			{
+				Py_DECREF(o1);
+				Py_DECREF(o2);
+				return NULL;
+			}
+
+			max_size = _limb_sub(o1->limbs, o1->ob_size, o2->limbs, o2->ob_size, ret->limbs);
+			ret->ob_size = max_size;
+			ret->limb_count = (max_size + LOG -1)/LOG;
+			
+		}
+	}
+	else
+	{
+		long max_size;
+		max_size = o1->ob_size > o2->ob_size ? o1->ob_size : o2->ob_size;
+		ret = _NEW_decimalobj(max_size + 1, o1->sign, o1->exp);	/* we may have extra digit */
+		if(!ret)
+		{
+			Py_DECREF(o1);
+			Py_DECREF(o2);
+			return NULL;
+		}
+		
+		max_size = _limb_add(o1->limbs, o1->ob_size, o2->limbs, o2->ob_size, ret->limbs);
+		ret->ob_size = max_size;
+		ret->limb_count = (max_size + LOG -1)/LOG;
+//		Py_DECREF(o1);
+//		Py_DECREF(o2);
+//		if(shouldround)
+//		{
+//			fixed = _decimal_fix(ret, ctx);
+//			Py_DECREF(ret);
+//			if(!fixed)
+//			{
+//				return NULL;
+//			}
+//			return fixed;
+//		}
+//		return ret;
+	}
+
+	Py_DECREF(o1);
+	Py_DECREF(o2);
+	
+	if(shouldround)
+	{
+		decimalobject *fixed;
+		fixed = _decimal_fix(ret,ctx);
+		Py_DECREF(ret);
+		if(!fixed)
+			return NULL;
+		return fixed; 
+	}
+	
+	return ret;
+	
+error:
+	Py_XDECREF(o1);
+	Py_XDECREF(o2);	
+	return NULL;
 }
 DECIMAL_SPECIAL_2FUNC(decimal_add)
 
@@ -2693,10 +2986,14 @@
         return NULL;
     /* remove sign */
     if (str[0] == '+')
+	{
         str++;
+		len --;
+	}
     else if (str[0] == '-') {
         str++;
         literalsign = 1;
+		len --;
     }
     if (tolower(str[0]) == 'n' &&
         tolower(str[1]) == 'a' &&
@@ -2734,7 +3031,24 @@
         return NULL;
     for (i = 0; i < size; i++)
         new->digits[i] = *(start+i) -48;
-    return new;
+	for(i=0;i<new->limb_count;i++)
+		new->limbs[i] = 0;
+
+	long mult = 1;
+	long limb = 0;
+	for(i = size -1; i>=0; i--)
+	{
+		assert(limb < new->limb_count);
+		new->limbs[limb] += mult * (start[i] - '0');
+		mult *= 10;
+		if(mult == BASE)
+		{
+			mult = 1;
+			limb ++;
+		}
+	}
+
+	return new;
 }
 
 
@@ -3370,6 +3684,7 @@
 static PyMemberDef _decimal_members[] = {
     {"_sign", T_INT, offsetof(decimalobject, sign), 0},
     {"_exp", T_LONG, offsetof(decimalobject, exp), 0},
+	{"_size", T_LONG, offsetof(decimalobject, ob_size), 0},
     {NULL}
 };
 
@@ -3668,8 +3983,13 @@
 static PyObject *
 context_clear_flags(contextobject *self)
 {
-    self->flags = 0;
-    Py_RETURN_NONE;
+//    self->flags = 0;
+//	PyDict_Clear(self->flags);
+	int i;
+	for(i = 0;i<NUMSIGNALS;i++)
+		_set_flag(self->flags,i,0);
+	
+	Py_RETURN_NONE;
 }
 
 
@@ -3810,6 +4130,32 @@
    be created by macros. */
 
 static PyObject *
+context_apply(contextobject *self, PyObject *args, PyObject *kwds)
+{
+	static char *kwlist[] = {"a", 0};
+	decimalobject *a;
+
+	if(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &a))
+		return NULL;
+
+	if(!PyDecimal_Check(a))
+	{
+		PyErr_SetString(PyExc_ValueError, "a must be Decimal object");
+		return NULL;
+	}
+	
+	decimalobject *tmp = _decimal_fix(a, self);
+	if(!tmp)
+		return NULL;
+
+	PyObject *ret = decimal_str(tmp);
+	Py_DECREF(tmp);
+
+	return ret;
+	
+}
+	
+static PyObject *
 context_power(contextobject *self, PyObject *args)
 {               
     PyObject *a, *b, *c;                                                
@@ -4227,6 +4573,8 @@
 /* XXX: all that PyCFunction casting might not be necessary */
 
 static PyMethodDef context_methods[] = {
+	{"_apply",			(PyCFunction)context_apply,
+	 METH_VARARGS | METH_KEYWORDS},
     {"clear_flags",     (PyCFunction)context_clear_flags,
      METH_NOARGS},
     {"copy",            (PyCFunction)context_copy,
@@ -4457,6 +4805,11 @@
     _flags = PyDict_New();
     if (!_flags) goto err;
 
+	for(i=0; i < NUMSIGNALS; i++)	/* XXX don't know if it's ok! */
+	{
+		_set_flag(_flags,i,0);
+	}
+
     if (pyflags == NULL) {
         /* don't copy flags from default context */
     } else if (PyDict_Check(pyflags)) {


More information about the Python-checkins mailing list