[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