A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from http://svn.python.org/projects/sandbox/trunk/decimal-c/_decimal.c below:

/* C implementation of the decimal module. * * Partly written for the Need for Speed sprint in * Iceland by Georg Brandl and Jack Diederich. * * Copyrighted to the Python Software Foundation. */ /* Notable incompatibilities between this and the original decimal.py: - Rounding constants are integers. - There's no handle method on the exceptions. - Context methods don't accept keyword arguments (they're all called a and b anyways). This probably will be changed. - Special values are represented by special sign values, so the results of as_tuple() differ. - Many internal "underscore" methods are not accessible on the object. - The Python version sometimes gets the context in the middle of a method which leads to some calls succeeding even if there is no valid context. Notable kludges necessary retain some compatibility: - decimal.py Contexts used to have three dicts, traps, flags and _ignored, to store whether an error occurred or whether to raise an exception in this case. I started with them being bitmasks until we realized that Python code expects them to be dicts and wants to modify those, so we added back dictobjects to decimalobject. I guess that this causes slowdown here and there, and the proper way to do this is to subclass dict to call back some method on assignment. (This is better done in Python code, I guess.) - Some special (__xxx__) methods in the Python code took additional arguments used internally. Since that's not possible, there's a _do_decimal_xxx along with a decimal_xxx, see below. - Arithmetic functions (like __add__, __mul__) have only one operand so, we cannot use custom context. This is not yet optimized C code, but mostly translated Python that was not really made for translating, though it originally should have been. Areas which need improvement are marked with XXX throughout the code. The functions could use some more comments, I sometimes didn't even transfer every comment in the Python version. Refcounting and more complicated structure make even more comments necessary. Each function whose name doesn't start with an underscore is supposed to be stowed in some TypeObject struct. Note that the argument and return types of functions are also not very consistent. Casting things around is necessary, but I'm not always sure where the right place for that to happen is. (If anyone calls C a strongly typed language again, hit him on the head.) We were undecided whether to offer a public C API (PyDecimal_FromString etc.) If you want to do this, look at the datetime module. This code still needs some clean-ups and improvements, ie. Context isn't safe for subclassing. Context should become gc object. 'digits' will be removed from decimalobject. Also, I am not really sure, if code is readable, especially after switching from longs to exp_t - this will need a little work too. */ #include "Python.h" #include "modsupport.h" #include "structmember.h" #include "decimal.h" #include /* Important note: * The code is meant to work well and without errors, not yet to be efficent. * I will start optimizing code, when I will be quite sure, everything * works fine. Real slow code, that should be replaced have comment * "SLOW". */ /* integer arithmetic */ /* Little explanation: * Digits are grouped into limbs, that is, every limb keeps LOG digits. * Arithmetic perfomred on those limbs are very the same that in base 10 * but insetad of dealing "digits" 0-9, we deal with "digits" 0-(BASE -1) * Note, that first limb is least significant. * Further in code when I say "limbs" I mean one calculation unit, and * when I say "digit" I mean digit between 0-9 (limb consists of LOG digits). */ /* NOTE: there is a little inconcistency with size arguments, sometimes they * say how many limbs, sometimes how many digits, it's going to change XXX*/ /* takes new_digits from self[], starting from start_at digit, *digits* not limbs */ /* returns digit after that we returned (useful for rounding) */ /* just returns how many significant limbs */ static long _limb_size(long *self, long limbs) { while(!self[limbs-1] && limbs >1) limbs --; return limbs; } /* just like binary <<, but base BASE not 2 */ static void _limb_move_left(long *self, long limbs, long count) { long i; assert(count >= 0); for (i = limbs - 1; i - count >= 0; i--) self[i] = self[i-count]; for (i = count-1; i >= 0; i--) self[i] = 0; } static long _limb_first_n_digits(long *self, long ndigits, long start_at, long *new, long new_digits) { long start_pos = ndigits - (start_at + new_digits); /* where we start counting from left */ long pos = 0; /* we are here */ long self_limb = 0; /* we start at first digit of first limb */ long self_mult = 1; long new_limb = 0; long new_mult = 1; long last_digit = 0; /* digit from start_pos -1 */ long i; long diff; long actual_limb; for (i = 0; i < new_digits; i += LOG) new[i/LOG] = 0; while (pos < start_pos) /* we need to set pos to 0 */ { if (pos == start_pos - 1) /* that's our last_digit */ { long tmp = self[self_limb]; tmp %= self_mult * 10; tmp /= self_mult; last_digit = tmp; } self_mult *= 10; if (self_mult == BASE) { self_limb ++; self_mult = 1; } pos ++; } diff = start_pos; while (diff < 0) /* put 0s to new */ { new[new_limb] = 0; new_mult *= 10; if (new_mult == BASE) { new_limb ++; new_mult = 1; } diff ++; } /* now we will just copy from self to digit */ actual_limb = self[self_limb]; actual_limb /= self_mult; /* while don't go out of self and new */ while (pos < ndigits && pos - start_pos < new_digits) { long x; if (self_mult == 1) actual_limb = self[self_limb]; x = actual_limb % 10; new[new_limb] += x * new_mult; new_mult *= 10; if (new_mult == BASE) { new_limb ++; new_mult = 1; } actual_limb /= 10; self_mult *= 10; if(self_mult == BASE) { self_mult = 1; self_limb ++; } pos ++; } /* rest is 0 */ return last_digit; } /* cuts least significant digit */ static void _limb_cut_one_digit(long *self, long ndigits) { long limb_count = ndigits/LOG + ((ndigits%LOG) != 0); long i; self[0] /= 10; for(i = 1;i 0; i--) { self[i] *= 10; self[i] += self[i-1] / (BASE/10); self[i-1] %= (BASE/10); } self[0] *= 10; self[0] += dig; } static void _limb_fill(long *self, long ndigits, long x) { long full_limb = 0; long mult = 1; long i; long limbs; while(mult != BASE) { full_limb += x * mult; mult *= 10; } limbs = ndigits / LOG; for(i = 0;i = ndigits || i < 0) return 0; tmp = self[limb]; while (pos) { tmp/=10; pos --; } return tmp%10; } /* returns how many significant digits */ static long _limb_size_s(long *self, long size) { long limbs = (size + LOG -1)/ LOG; long slimbs; long size_at_most; slimbs = _limb_size(self, limbs); size_at_most = slimbs * LOG; while(_limb_get_digit(self, size_at_most, 0) == 0 && size_at_most > 1) size_at_most --; return size_at_most; } /* compares, self and other must be normalized */ 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; } /* just like above, but no need to proper sizes * (may be extra zeros at most significant positions) */ static int _limb_compare_un(long *self, long slimbs, long *other, long olimbs) { long n_slimbs, n_olimbs; n_slimbs = _limb_size(self, slimbs); n_olimbs = _limb_size(other, olimbs); return _limb_compare(self, n_slimbs, other, n_olimbs); } 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 = 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); } /* substracts small from big, and stores result in out, sizes are in * digits, not limbs */ 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; long left_limbs; long left_digits; for(i=0;i = BASE) { assert (i+1 <=flimbs); out[i+1] += out[i]/BASE; out[i] %= BASE; } } if (out[flimbs]) return flimbs+1; return flimbs; } /* temporary solution, will be speeded up */ static long _limb_multiply_core(long *first, long flimbs, long *second, long slimbs, long *out) { long max_limbs = flimbs + slimbs; long i,j; for(i = 0;i = BASE) { out[i+j+1] += out[i+j]/BASE; out[i+j] %= BASE; } } for(i = 0;i = BASE) { assert(i+1 < max_limbs); out[i+1] += out[i] / BASE; out[i] %= BASE; } } while(!out[max_limbs -1] && max_limbs > 1) max_limbs --; return max_limbs; } static long _limb_multiply(long *first, long fsize, long *second, long ssize, long *out) { long used_limbs; long flimbs, slimbs; long digits_at_most; flimbs = (fsize + LOG - 1)/LOG; slimbs = (ssize + LOG - 1)/LOG; used_limbs = _limb_multiply_core(first, flimbs, second, slimbs, out); digits_at_most = used_limbs * LOG; if(!digits_at_most) return 1; while(_limb_get_digit(out, digits_at_most, 0) == 0 && digits_at_most >1) digits_at_most --; return digits_at_most; } /* performs dividing, returns relative position of dot to last limb, rest has * at most as much elements as second + 1, it calculates prec *significant* * limbs, first must not be 0 */ /* XXX it's naive dividing, very slow */ /* min_new_pos tells, when we should stop dividing, useful for integer division * make it > flimbs - 2, and it will have no impact*/ /* function assumes that rest is filled with 0s */ static long _limb_divide(long *first, long flimbs, long *second, long slimbs, long *out, long prec, long *rest, long min_new_pos) { long rlimbs = 1; /* significant limbs of rest */ long is_significant = 0; /* tells whether non_zero limb has already showed up */ long out_pos = prec - 1; /* where we write result starts at prec - 1 stops at 0*/ long new_pos = flimbs - 2; /* where is new digit to add at the end of rest, if new_pos<0 add 0 */ long *tmp = (long*)malloc(sizeof(long) * (slimbs+1)); if (!tmp) { PyErr_NoMemory(); return 0; } rest[0] = first[flimbs-1]; while(1) { long candidate = 0; long cmp; long up, down; up = BASE; down = 0; while(1) { /* long tmp[slimbs+1]; */ long tmp_limbs; long diff; long mid; diff = up - down; mid = down + diff/2; tmp_limbs = _limb_multiply_int(second, slimbs, mid, tmp); cmp = _limb_compare_un(rest, rlimbs, tmp, tmp_limbs); if (cmp == 0) { up = mid + 1; down = mid; } if (cmp == -1) { up = mid; } if (cmp == 1) { down = mid; } if (down == up-1) { tmp_limbs = _limb_multiply_int(second, slimbs, down, tmp); rlimbs = _limb_sub_sl(rest, rlimbs, tmp, tmp_limbs); candidate = down; break; } } /* while ((cmp = _limb_compare_un(rest, rlimbs, second, slimbs)) >= 0) { candidate ++; rlimbs = _limb_sub_sl(rest, rlimbs, second, slimbs); } */ if (candidate) is_significant = 1; if (is_significant) { out[out_pos] = candidate; out_pos --; } if (out_pos < 0) break; assert(rlimbs <= slimbs +1); _limb_move_left(rest, slimbs+1, 1); if(new_pos >= 0) rest[0] = first[new_pos]; else rest[0] = 0; rlimbs = _limb_size(rest, slimbs + 1); if (min_new_pos == new_pos) { long last_written = out_pos + 1; long i; long rem_limbs = flimbs > slimbs + 1 ? flimbs : slimbs + 1; /* XXX SLOW */ for (i = new_pos - 1; i>=0; i--) { _limb_move_left(rest, rem_limbs, 1); rest[0] = first[i]; } for (i = 0; i + last_written < prec; i++) out[i] = out[i+last_written]; for (i = prec - last_written; i < prec ;i++) out[i] = 0; free(tmp); return new_pos; } new_pos --; } free(tmp); return new_pos; } /* integer division, returns how many limbs result has */ static long _limb_integer_divide(long *first, long flimbs, long *second, long slimbs, long *out, long *remainder) { long rpos; int i; rpos = _limb_divide(first, flimbs, second, slimbs, out, flimbs, remainder, -1); /* this is index of first limb that is before dot */ rpos ++; for(i = rpos; i < flimbs; i ++) out[i-rpos] = out[i]; return _limb_size(out, flimbs - rpos); } /* computes floor(sqrt(first)), first and result are integers, * res should have at least (flimbs+1)/2 + 1 size */ static long _limb_sqrt(long *first, long flimbs, long *res) { /* it's Newton's method, we start with x_1 = first */ int i; int cmp_res; int rlimbs; int qlimbs; long *remainder; long *quot = (long*)malloc(sizeof(long) * (flimbs + 1)); remainder = (long*)malloc(sizeof(long) * (flimbs + 1)); /* upper bound */ rlimbs = (flimbs + 1) / 2 + 1; for(i = 0; i < rlimbs-1; i++) res[i] = 0; res[rlimbs-1] = 1; while(1) { /* quot = floor(first / res */ memset(remainder, 0, (flimbs + 1) * sizeof(long)); qlimbs = _limb_integer_divide(first, flimbs, res, rlimbs, quot, remainder); /* if rest <= quot then break - we stop here because otherwise result would grow * and become ceiling instead of floor */ cmp_res = _limb_compare(res, rlimbs, quot, qlimbs); if(cmp_res <= 0) break; /* res = res + quot */ rlimbs = (_limb_add(res, LOG * rlimbs, quot, LOG * qlimbs, res) + LOG - 1) / LOG; /* res = floor(res / 2) */ for(i=rlimbs-1;i>0;i--) { res[i-1] += (res[i] &1) * BASE; res[i] >>= 1; } res[0] >>= 1; if(rlimbs > 1 &&!res[rlimbs-1]) rlimbs --; } free(remainder); free(quot); return rlimbs; } /* static long _limb_normalize(long *first, long size) { long i; long new_size; for (i = 0; i < size - 1; i++) { first[i+1] += first[i] / BASE; first[i] %= BASE; } new_size = size; while (!first[new_size - 1] && new_size > 1) new_size --; return new_size; } */ #ifdef BIG_EXP /* TODO I have to *limit* passing arguments by value, * because it really slows down implementation. Most probably * it will also dynamicly switch between C longs/bigint arithmetic * to make things go faster */ static exp_t exp_from_i(long a) { exp_t ret; long i; ret.limbs[0] = a; ret.sign = 0; if (a < 0) { ret.sign = 1; ret.limbs[0] *= -1; } for (i=0 ; i sign = 0; exp->size = 1; if (buf[0] == '-') { exp->sign = 1; buf ++; } else if (buf[0] == '+') buf ++; len = strlen(buf); if (!len) return 0; while (buf[0] == '0') { buf ++; } len = strlen(buf); mul = 1; limb = 0; for (i = len-1; i>=0 ;i--) { if (buf[i] < '0' && buf[i] > '9') return 0; exp->size = limb + 1; if (limb >= EXP_LIMB_COUNT) return 0; if (mul == 1) exp->limbs[limb] = buf[i] - '0'; else exp->limbs[limb] += mul * (buf[i] - '0'); mul *= 10; if (mul == BASE) { limb ++; mul = 1; } } return 1; } static int exp_sprintf(char *buf, exp_t exp) { int written = 0; int tmp; long i; if (exp.sign) { buf[0] = '-'; buf ++; written ++; } tmp = sprintf(buf, "%ld", exp.limbs[exp.size-1]); buf += tmp; written += tmp; for (i = exp.size - 2; i>=0; i--) { tmp = sprintf(buf, "%."LOG_STR"ld", exp.limbs[i]); buf += tmp; written += tmp; } buf[0] = '\0'; return written; } /* must check errors with PyErr_Occurred() */ static exp_t exp_from_pyobj(PyObject *a) { if (PyInt_Check(a)) { return exp_from_i(PyInt_AS_LONG(a)); } else if (PyLong_Check(a)) { char *bufer; PyObject *strval; exp_t ret; Py_ssize_t buf_len = 0; strval = PyObject_Str(a); if (!strval) return ret; if (PyObject_AsCharBuffer(strval, (const char **)&bufer, &buf_len) == -1) { Py_DECREF(strval); return ret; } if (exp_sscanf(bufer, &ret) != 1) { Py_DECREF(strval); PyErr_SetString(PyExc_TypeError, "exponent must be integer value."); return ret; } Py_DECREF(strval); return ret; } else { exp_t ret; PyErr_SetString(PyExc_TypeError, "exponent must be integer value."); return ret; } } /* there is no overflow checking !*/ static long exp_to_i(exp_t exp) { long mult; long i; long ret = 0; long mul = 1; mult = 1; for (i=0; i sign == b.sign) { a->size = _limb_add(a->limbs, a->size * LOG, b.limbs, b.size * LOG, a->limbs); a->size = (a->size + LOG - 1) / LOG; } else { int cmp; cmp = _limb_compare(a->limbs, a->size, b.limbs, b.size); if (cmp == 0) { a->limbs[0] = 0; a->size = 1; a->sign = 0; } if (cmp == 1) { a->size = _limb_sub_sl(a->limbs, a->size, b.limbs, b.size); } if (cmp == -1) { exp_t tmp; tmp.size = _limb_sub(b.limbs, b.size * LOG, a->limbs, a->size * LOG, tmp.limbs); tmp.size = (tmp.size + LOG -1) / LOG; *a = tmp; a->sign = b.sign; } } return *a; } static exp_t exp_inp_sub(exp_t *a, exp_t b) { exp_t tmp_b = b; tmp_b.sign ^= 1; if (tmp_b.limbs[0] == 0 && tmp_b.size == 1) tmp_b.sign =0; return exp_inp_add(a, tmp_b); } static exp_t exp_add(exp_t a, exp_t b) { return exp_inp_add(&a, b); } static exp_t exp_sub(exp_t a, exp_t b) { return exp_inp_sub(&a, b); } static exp_t exp_add_i(exp_t a, long b) { return exp_add(a, exp_from_i(b)); } static exp_t exp_sub_i(exp_t a, long b) { return exp_sub(a, exp_from_i(b)); } /* static exp_t exp_inp_add_i(exp_t *a, long b) { return exp_inp_add(a, exp_from_i(b)); } */ static exp_t exp_inp_sub_i(exp_t *a, long b) { return exp_inp_sub(a, exp_from_i(b)); } static exp_t exp_inc(exp_t *a) { return exp_inp_add(a, exp_from_i(1)); } static exp_t exp_dec(exp_t *a) { return exp_inp_sub(a, exp_from_i(1)); } static exp_t exp_mul(exp_t a, exp_t b) { exp_t ret; ret.size = _limb_multiply_core(a.limbs, a.size, b.limbs, b.size, ret.limbs); ret.sign = a.sign ^ b.sign; if (ret.size == 1 && ret.limbs[0] == 0) ret.sign = 0; return ret; } static exp_t exp_mul_i(exp_t a, long b) { return exp_mul(a, exp_from_i(b)); } static exp_t exp_div_i(exp_t a, long b, long *remainder) { exp_t ret; long i; long mult = 1; for (i=0 ; i =0; i--) { *remainder *= mult; *remainder += a.limbs[i]; ret.limbs[i] = *remainder / b; *remainder %= b; } ret.size = a.size; while (!ret.limbs[ret.size-1] && ret.size > 1) ret.size --; ret.sign = a.sign ^ (b < 0); if (ret.limbs[0] == 0 && ret.size == 1) ret.sign = 0; return ret; } static int exp_mod_i(exp_t a, long b) { long remainder; exp_div_i(a, b, &remainder); return remainder; } static int exp_cmp(exp_t a, exp_t b) { int cmp; if (a.sign != b.sign) { if (a.sign) return -1; else return 1; } cmp = _limb_compare(a.limbs, a.size, b.limbs, b.size); if (a.sign) return -cmp; else return cmp; } /* static int exp_cmp_i(exp_t a, long b) { return exp_cmp(a, exp_from_i(b)); } */ static int exp_g(exp_t a, exp_t b) { int cmp = exp_cmp(a, b); return cmp == 1; } static int exp_g_i(exp_t a, long b) { return exp_g(a, exp_from_i(b)); } static int exp_l(exp_t a, exp_t b) { int cmp = exp_cmp(a, b); return cmp == -1; } static int exp_l_i(exp_t a, long b) { return exp_l(a, exp_from_i(b)); } static int exp_eq(exp_t a, exp_t b) { int cmp = exp_cmp(a, b); return cmp == 0; } static int exp_eq_i(exp_t a, long b) { return exp_eq(a, exp_from_i(b)); } static int exp_ne(exp_t a, exp_t b) { int cmp = exp_cmp(a, b); return cmp != 0; } /* static int exp_ne_i(exp_t a, long b) { return exp_ne(a, exp_from_i(b)); } */ static int exp_ge(exp_t a, exp_t b) { int cmp = exp_cmp(a, b); return (cmp == 1) || (cmp == 0); } static int exp_ge_i(exp_t a, long b) { return exp_ge(a, exp_from_i(b)); } static int exp_le(exp_t a, exp_t b) { int cmp = exp_cmp(a, b); return (cmp == -1) || (cmp == 0); } static int exp_le_i(exp_t a, long b) { return exp_le(a, exp_from_i(b)); } static int exp_is_zero(exp_t a) { return a.limbs[0] == 0 && a.size == 1; } static int exp_is_neg(exp_t a) { return !exp_is_zero(a) && a.sign == 1; } static int exp_is_pos(exp_t a) { return !exp_is_zero(a) && a.sign == 0; } static exp_t exp_neg(exp_t a) { a.sign ^= 1; if (a.limbs[0] == 0 && a.size == 1) a.sign = 0; return a; } static exp_t exp_min(exp_t a, exp_t b) { int cmp = exp_cmp(a, b); if (cmp == 1) return b; else return a; } static exp_t exp_max(exp_t a, exp_t b) { int cmp = exp_cmp(a, b); if(cmp == 1) return a; else return b; } static exp_t exp_floordiv_i(exp_t a, long b) { long remainder; exp_t ret; ret = exp_div_i(a, b, &remainder); if (remainder && exp_is_neg(a)) exp_dec(&ret); return ret; } #else /* TODO a little mess here ;P */ #define exp_add_i(exp, a) ((exp) + (a)) #define exp_add(a, b) ((a) + (b)) #define exp_inc(a) ((*a)++) #define exp_dec(a) ((*a)--) #define exp_is_zero(a) ((a) == 0) #define exp_is_neg(a) ((a) < 0) #define exp_is_pos(a) ((a) > 0) #define exp_inp_add(a,b) ((*(a)) += (b)) #define exp_to_int(a) (a) #define exp_to_i(a) (a) #define exp_from_i(a) (a) #define exp_sub_i(exp, a) ((exp) - (a)) #define exp_sub(a, b) ((a) - (b)) #define exp_g(a, b) ((a) > (b)) #define exp_l(a, b) ((a) < (b)) #define exp_ge(a, b) ((a) >= (b)) #define exp_eq(a, b) ((a) == (b)) #define exp_eq_i(a, b) ((a) == (b)) #define exp_ne(a, b) ((a) != (b)) #define exp_g_i(a, b) ((a) > (b)) #define exp_l_i(a, b) ((a) < (b)) #define exp_ge_i(a, b) ((a) >= (b)) #define exp_le_i(a, b) ((a) <= (b)) #define exp_mod_i(a, b) ((a) % (b)) #define exp_ge_i(a, b) ((a) >= (b)) #define exp_floordiv_i(a,b) ((a) / (b) - ((a) % (b) && (a) < 0)) #define exp_inp_sub(a, b) ((*(a)) -= (b)) #define exp_inp_sub_i(a, b) ((*(a)) -= (b)) #define exp_sprintf(a, e) (sprintf(a, "%ld", e)) #define exp_sscanf(a, e) (sscanf(a, "%ld", e)) #define exp_min(a, b) ((a) < (b) ? (a) : (b)) #define exp_max(a, b) ((a) > (b) ? (a) : (b)) #define exp_neg(a) (-(a)) #define exp_mul_i(a, b) ((a) * (b)) #define exp_to_pyobj(a) (PyInt_FromLong(a)) #define exp_from_pyobj(a) (PyInt_AsLong(a)) #endif /* helpful macros ************************************************************/ /* if we later decide to call this module "squeaky" */ #define MODULE_NAME "_decimal" #define INITFUNC_NAME init_decimal #define contextobject PyDecimalContextObject #define decimalobject PyDecimalObject /* Checks */ #define ISSPECIAL(op) ((op)->sign >= SIGN_POSINF) /* 1 if NaN, 2 if sNan, 0 else */ #define GETNAN(op) ( ( (op)->sign == SIGN_POSNAN || (op)->sign == SIGN_NEGNAN ) ? \ 1 : ( ( (op)->sign == SIGN_POSSNAN || (op)->sign == SIGN_NEGSNAN ) ? 2 : 0 ) ) #define ISINF(op) ( ( (op)->sign == SIGN_POSINF || (op)->sign == SIGN_NEGINF) ? \ ((op)->sign == SIGN_POSINF ? 1 : -1) : 0 ) /* Exponent calculations */ /* XXX: overflow checking? */ #define ETINY(ctx) (exp_sub_i((ctx)->Emin, (ctx)->prec - 1)) #define ETOP(ctx) (exp_sub_i((ctx)->Emax, (ctx)->prec - 1)) /*#define ADJUSTED(dec) ((dec)->exp + (dec)->ob_size - 1) */ #define ADJUSTED(dec) (exp_add_i((dec)->exp, (dec)->ob_size - 1)) /* constant values ***********************************************************/ /* sign values */ /* Since we do (decimal->sign & 1) all over the place, it is important that negative signs have the first bit set. */ #define SIGN_POS 0 #define SIGN_NEG 1 #define SIGN_POSINF 2 #define SIGN_NEGINF 3 #define SIGN_POSNAN 4 #define SIGN_NEGNAN 5 #define SIGN_POSSNAN 6 #define SIGN_NEGSNAN 7 /* Bigest exponent in transcendetial functions */ #define MAX_MATH 999999 /* Rounding constants */ /* for context->rounding */ #define ROUND_DOWN 0 #define ROUND_UP 1 #define ROUND_HALF_DOWN 2 #define ROUND_HALF_EVEN 3 #define ROUND_HALF_UP 4 #define ROUND_FLOOR 5 #define ROUND_CEILING 6 #define ROUND_05UP 7 #define VALID_ROUND(x) ((x) <= ROUND_05UP && (x) >= ROUND_DOWN) /* for context->rounding_dec */ #define ALWAYS_ROUND 0 #define NEVER_ROUND 16 #define VALID_ROUND_DEC(x) ((x) == ALWAYS_ROUND || (x) == NEVER_ROUND) /* Context signals */ /* The naming (conditions vs. signals vs. errors vs. exceptions) is not very consistent. */ #define NUMSIGNALS 8 #define S_CLAMPED 0 #define S_INV_OPERATION 1 #define S_DIV_BY_ZERO 2 #define S_INEXACT 3 #define S_ROUNDED 4 #define S_OVERFLOW 5 #define S_UNDERFLOW 6 #define S_SUBNORMAL 7 /* WTF ?? */ /* Other conditions. If they occur and raise an exception, it will be InvalidOperation. Also, context->flags, ->traps and ->ignored will only contain entries for the signals defined above. */ #define NUMCONDITIONS 4 #define C_DIV_IMPOSSIBLE 8 #define C_DIV_UNDEFINED 9 #define C_INV_CONTEXT 10 #define C_CONV_SYNTAX 11 /* Exceptions ****************************************************************/ /* Indentation below hints at inheritance. */ static PyObject *DecimalException; /* never actually raised */ static PyObject *Clamped; static PyObject *InvalidOperation; static PyObject *ConversionSyntax; static PyObject *DivisionImpossible; static PyObject *DivisionUndefined; /* also inherits ZeroDivisionError */ static PyObject *InvalidContext; static PyObject *DivisionByZero; /* also inherits ZeroDivisionError */ static PyObject *Inexact; static PyObject *Rounded; static PyObject *Overflow; /* also inherits Inexact */ static PyObject *Underflow; /* also inherits Inexact, Subnormal */ static PyObject *Subnormal; /* this contains these exception types, indexed by the S_* and C_* constants defined above. Initialized in init_decimal. */ static PyObject *errors[NUMSIGNALS+NUMCONDITIONS]; /* for context->flags, ->traps, ->ignored *************************************/ /* XXX: lazy error checking */ static int _is_flag_set(PyObject *d, int f) { PyObject *item = PyDict_GetItem(d, errors[f]); if (item) return PyObject_IsTrue(item); return 0; } static int _set_flag(PyObject *d, int f, int v) { PyObject *i = PyInt_FromLong(v); PyObject *item; int res = 0; if (!i) return -1; item = PyDict_GetItem(d, errors[f]); if (item) res = PyObject_IsTrue(item); if (PyDict_SetItem(d, errors[f], i) < 0) res = -1; Py_DECREF(i); return res; } /* Forwarding ****************************************************************/ /* some of them could be removed if the functions were declared in the right order */ static contextobject *getcontext(void); static int setcontext(contextobject *); static PyObject *context_new(PyTypeObject *, PyObject *, PyObject *); static int decimal_nonzero(decimalobject *); static decimalobject *_decimal_get_copy(decimalobject *); static decimalobject *_decimal_from_pylong(PyTypeObject *, PyObject *, contextobject *); static contextobject * context_copy(contextobject *); static PyObject *decimal_from_long(PyTypeObject *, long); static PyObject *decimal_str(decimalobject *); static PyObject *_do_decimal_str(decimalobject *, contextobject *, int); static decimalobject *_new_decimalobj(PyTypeObject *, long, char, exp_t); static decimalobject *_do_decimal_subtract(decimalobject *, decimalobject *, contextobject *); static contextobject *context_shallow_copy(contextobject *); static PyObject *context_ignore_all_flags(contextobject *); static PyObject *context_regard_flags(contextobject *, PyObject*); static decimalobject *_do_decimal_absolute(decimalobject *, contextobject *, int); static PyObject *decimal_int(decimalobject*); static decimalobject *_do_decimal_add(decimalobject *, decimalobject *, contextobject *); static PyObject *_do_decimal__divide(decimalobject *, decimalobject *, int, contextobject *); static decimalobject *_decimal_fromliteral(PyTypeObject *, char *str, long, contextobject *); static decimalobject *_do_decimal_multiply(decimalobject *, decimalobject *, contextobject *); 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 *********************************************************/ /* Raise the exception with expl if cond is trapped and not ignored. When raising, return onerror. */ #define HANDLE_ERROR(ctx, cond, expl, onerror) \ int err = (cond >= NUMSIGNALS ? S_INV_OPERATION : cond); \ if (! _is_flag_set(ctx->ignored, err)) { \ _set_flag(ctx->flags, err, 1); \ if (_is_flag_set(ctx->traps, err)) { \ PyErr_SetString(errors[err], (expl ? expl : "")); \ return onerror; \ } \ } static int handle_Clamped(contextobject *ctx, char *expl) { HANDLE_ERROR(ctx, S_CLAMPED, expl, -1); return 0; } static decimalobject * handle_InvalidOperation(PyTypeObject *type, contextobject *ctx, char *expl, decimalobject *thing) { decimalobject *res, *res2; long sign, i; HANDLE_ERROR(ctx, S_INV_OPERATION, expl, NULL); if (thing == NULL) { Py_INCREF(PyDecimal_NaN); return (decimalobject *)PyDecimal_NaN; } assert(PyDecimal_Check(thing)); /* we want to return a NaN, but keep diagnostics */ if(thing->sign&1) /* neg */ 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]; res2 = _decimal_fix_nan(res, ctx); if(!res2) return NULL; Py_DECREF(res); return res2; } static decimalobject * handle_ConversionSyntax(PyTypeObject *type, contextobject *ctx, char *expl) { HANDLE_ERROR(ctx, C_CONV_SYNTAX, expl, NULL); Py_INCREF(PyDecimal_NaN); return PyDecimal_NaN; } static decimalobject * handle_DivisionByZero(PyTypeObject *type, contextobject *ctx, char *expl, long sign, int two) { decimalobject *res; HANDLE_ERROR(ctx, S_DIV_BY_ZERO, expl, NULL); if (sign & 1) res = PyDecimal_NegInf; else res = PyDecimal_Inf; Py_INCREF(res); return res; } static PyObject * handle_DivisionImpossible(PyTypeObject *type, contextobject *ctx, char *expl) { HANDLE_ERROR(ctx, C_DIV_IMPOSSIBLE, expl, NULL); /* Py_INCREF(PyDecimal_NaN); */ return Py_BuildValue("(OO)", PyDecimal_NaN, PyDecimal_NaN); } static PyObject * handle_DivisionUndefined(PyTypeObject *type, contextobject *ctx, char *expl, int two) { HANDLE_ERROR(ctx, C_DIV_UNDEFINED, expl, NULL); if (!two) { Py_INCREF(PyDecimal_NaN); return (PyObject*)PyDecimal_NaN; } else { return Py_BuildValue("(OO)", PyDecimal_NaN, PyDecimal_NaN); } } static int handle_Inexact(contextobject *ctx, char *expl) { HANDLE_ERROR(ctx, S_INEXACT, expl, -1); return 0; } static decimalobject * handle_InvalidContext(PyTypeObject *type, contextobject *ctx, char *expl) { HANDLE_ERROR(ctx, C_INV_CONTEXT, expl, NULL); Py_INCREF(PyDecimal_NaN); return PyDecimal_NaN; } static int handle_Rounded(contextobject *ctx, char *expl) { HANDLE_ERROR(ctx, S_ROUNDED, expl, -1); return 0; } static int handle_Subnormal(contextobject *ctx, char *expl) { HANDLE_ERROR(ctx, S_SUBNORMAL, expl, -1); return 0; } static decimalobject * handle_Overflow(PyTypeObject *type, contextobject *ctx, char *expl, long lsign) { decimalobject *res = 0; HANDLE_ERROR(ctx, S_OVERFLOW, expl, NULL); assert(lsign == 0 || lsign == 1); if (ctx->rounding == ROUND_HALF_UP || ctx->rounding == ROUND_HALF_EVEN || ctx->rounding == ROUND_HALF_DOWN || ctx->rounding == ROUND_UP) { res = (decimalobject *)(lsign == 0 ? PyDecimal_Inf : PyDecimal_NegInf); } else if (lsign == 0) { if (ctx->rounding == ROUND_CEILING) res = (decimalobject *)PyDecimal_Inf; else { res = _new_decimalobj(type, ctx->prec, lsign, exp_sub_i(ctx->Emax, ctx->prec - 1)); if (res) { _limb_fill(res->limbs, ctx->prec, 9); return res; } } } else if (lsign == 1) { if (ctx->rounding == ROUND_FLOOR) res = (decimalobject *)PyDecimal_NegInf; else { res = _new_decimalobj(type, ctx->prec, lsign, exp_sub_i(ctx->Emax, ctx->prec - 1)); if (res) { _limb_fill(res->limbs, ctx->prec, 9); return res; } } } Py_XINCREF(res); return res; } static int handle_Underflow(contextobject *ctx, char *expl) { HANDLE_ERROR(ctx, S_UNDERFLOW, expl, -1); return 0; } /* Decimal object methods *****************************************************/ /* NB: "type" is the real type of the object we're creating here. The first version didn't take a "type" arg and just used &PyDecimal_DecimalType instead. That's a bad idea since it doesn't allow for subclassing, so we're forced to carry around a typeobject everywhere where we might create new decimalobjects. */ static decimalobject * _new_decimalobj(PyTypeObject *type, long ndigits, char sign, exp_t exp) { decimalobject *new; char *arr = NULL; long *arr2 = NULL; long limb_c = ndigits / LOG; limb_c += (ndigits % LOG) > 0; if (ndigits > LONG_MAX) { PyErr_NoMemory(); return NULL; } arr2 = PyObject_MALLOC(limb_c * sizeof(long)); if(!arr2){ PyErr_NoMemory(); goto err; } new = (decimalobject *)type->tp_alloc(type, 0); if (new == NULL) goto err; new->sign = sign; new->exp = exp; new->ob_size = ndigits; new->limb_count = limb_c; new->limbs = arr2; return new; err: if (arr) PyObject_FREE(arr); if (arr2) PyObject_FREE(arr2); return NULL; } /* shortcut for use in methods */ #define _NEW_decimalobj(ndigits, sign, exp) \ _new_decimalobj(self->ob_type, ndigits, sign, exp) /* Helper functions ***********************************************************/ /* Check whether the number(s) aren't really numbers. * * If x and/or y are sNaN, signal, possibly storing the result * of handle() in res and returning 1 * or raising and returning -1 * If x and/or y are NaN, store NaN in res and return 1 * else return 0. * If an exception was set, return -1. */ static int _check_nans(decimalobject *x, decimalobject *y, contextobject *ctx, decimalobject **res) { int nan1 = GETNAN(x); int nan2 = 0; if (y) nan2 = GETNAN(y); if (nan1 || nan2) { if (nan1 == 2) { *res = handle_InvalidOperation(x->ob_type, ctx, "sNaN", x); if (*res == NULL) return -1; return 1; } else if (nan2 == 2) { *res = handle_InvalidOperation(y->ob_type, ctx, "sNaN", y); if (*res == NULL) return -1; return 1; } /* decimal_fix gives us new reference, no INCREF requiered */ if (nan1) *res = _decimal_fix_nan(x, ctx); else *res = _decimal_fix_nan(y, ctx); return 1; } return 0; } /* add 1eExponent. This is more efficient than add, used in rounding. */ static decimalobject * _decimal_increment(decimalobject *self, int round, contextobject *ctx) { long i; 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_get_copy(self); } new = _NEW_decimalobj(self->ob_size + 1, /* we possibly need a new digit */ self->sign, self->exp); if (!new) return NULL; for(i=0;i limb_count;i++) new->limbs[i] = self->limbs[i]; if(self->limb_count != new->limb_count) new->limbs[new->limb_count - 1] = 0; /* we have new limb */ new->limbs[0] ++; i = 0; while(new->limbs[i] >= BASE) { assert(i+1 < new->limb_count); new->limbs[i] -= BASE; new->limbs[i+1] ++; i++; } if(_limb_get_digit(new->limbs, new->ob_size, 0) == 0) new->ob_size --; new->limb_count = (new->ob_size + LOG - 1)/ LOG; return new; } /* Round towards 0, that is, truncate digits. */ static decimalobject * _round_down(decimalobject *self, long prec, exp_t expdiff, contextobject *ctx) { decimalobject *new = _NEW_decimalobj(prec, self->sign, exp_add(self->exp, expdiff)); if (!new) return NULL; _limb_first_n_digits(self->limbs, self->ob_size, 0, new->limbs, prec); return new; } /* Round away from 0. */ static decimalobject * _round_up(decimalobject *self, long prec, exp_t expdiff, contextobject *ctx) { long i; decimalobject *new = _NEW_decimalobj(prec, self->sign, exp_add(self->exp, expdiff)); decimalobject *new2 = NULL; if (!new) return NULL; _limb_first_n_digits(self->limbs, self->ob_size, 0, new->limbs, prec); for (i = prec; i < self->ob_size; i++) if(_limb_get_digit(self->limbs,self->ob_size, i) > 0){ /* SLOW */ new2 = _decimal_increment(new, 1, ctx); Py_DECREF(new); if (!new2) return NULL; if (new2->ob_size > prec) { _limb_cut_one_digit(new2->limbs,new2->ob_size); new2->ob_size--; new2->limb_count = (new2->ob_size + LOG -1)/LOG; exp_inc(&(new2->exp)); } return new2; } return new; } /* Actually round half up. Returns a new reference, either on tmp * or a new decimalobject, but steals one reference on tmp in turn * if it was passed in so that you can just return _do_round_half_up(...) * without DECREFing tmp afterwards. */ static decimalobject * _do_round_half_up(decimalobject *self, long prec, exp_t expdiff, contextobject *ctx, decimalobject *tmp) { decimalobject *new; assert(exp_g_i(expdiff, 0)); if(self->ob_size > prec && _limb_get_digit(self->limbs, self->ob_size, prec) >= 5){ /* SLOW */ new = _decimal_increment(tmp, 1, ctx); Py_DECREF(tmp); if (!new) return NULL; if (new->ob_size > prec) { _limb_cut_one_digit(new->limbs,new->ob_size); new->ob_size--; new->limb_count = (new->ob_size + LOG - 1)/LOG; exp_inc(&(new->exp)); } return new; } else { return tmp; } } /* Round 5 down. */ static decimalobject * _round_half_down(decimalobject *self, long prec, exp_t expdiff, contextobject *ctx) { long i, last; decimalobject *tmp; assert(exp_g_i(expdiff, 0)); tmp = _NEW_decimalobj(prec, self->sign, exp_add(self->exp, expdiff)); if (!tmp) return NULL; last = _limb_first_n_digits(self->limbs, self->ob_size, 0, tmp->limbs, 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 */ return _do_round_half_up(self, prec, expdiff, ctx, tmp); } /* self ends in 5000...., so tmp is okay */ return tmp; } return _do_round_half_up(self, prec, expdiff, ctx, tmp); } /* Round 5 to even, rest to nearest. */ static decimalobject * _round_half_even(decimalobject *self, long prec, exp_t expdiff, contextobject *ctx) { decimalobject *tmp; long i, last; assert(exp_g_i(expdiff, 0)); tmp = _NEW_decimalobj(prec, self->sign, exp_add(self->exp, expdiff)); if (!tmp) return NULL; last = _limb_first_n_digits(self->limbs, self->ob_size, 0, tmp->limbs, 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 */ return _do_round_half_up(self, prec, expdiff, ctx, tmp); } 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); } /* Round 5 up (away from 0). */ static decimalobject * _round_half_up(decimalobject *self, long prec, exp_t expdiff, contextobject *ctx) { decimalobject *tmp; tmp = _NEW_decimalobj(prec, self->sign, exp_add(self->exp, expdiff)); if (!tmp) return NULL; _limb_first_n_digits(self->limbs, self->ob_size, 0, tmp->limbs, prec); return _do_round_half_up(self, prec, expdiff, ctx, tmp); } /* Round up (regardless of sign) */ static decimalobject * _round_ceiling(decimalobject *self, long prec, exp_t 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_floor(decimalobject *self, long prec, exp_t 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); } /* 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_05up }; /* default: prec=-1, rounding=-1 (use context values), see the ROUND_* constants */ static decimalobject * _decimal_round(decimalobject *self, long prec, contextobject *ctx, int rounding) { decimalobject *new, *new2 = NULL; contextobject *ctx2 = NULL; long i; exp_t 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_get_copy(self); } if (rounding == -1) rounding = ctx->rounding; if (prec == -1) prec = ctx->prec; if (!VALID_ROUND(rounding)) { 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, exp_add_i(self->exp, self->ob_size - prec)); if (!new) return NULL; _limb_fill(new->limbs, new->ob_size,0); if (handle_Rounded(ctx, NULL) != 0) { Py_DECREF(new); return NULL; /* error was set */ } return new; } if (prec == 0) { new = _NEW_decimalobj(self->ob_size+1, self->sign, self->exp); if (!new) return NULL; _limb_first_n_digits(self->limbs, self->ob_size, -1, new->limbs, new->ob_size); prec = 1; } else if (prec < 0) { new = _NEW_decimalobj(2, self->sign, exp_add_i(self->exp, self->ob_size - prec - 1)); if (!new) return NULL; new->limbs[0] = 1; prec = 1; } else { new = _decimal_get_copy(self); if (!new) return NULL; } expdiff = exp_from_i(new->ob_size - prec); /* if (expdiff == 0) */ if(exp_is_zero(expdiff)) return new; else if (exp_is_neg(expdiff)) { /* we need to extend precision */ new2 = _NEW_decimalobj(prec, new->sign, exp_add(new->exp, expdiff)); if (!new2) { Py_DECREF(new); return NULL; } _limb_first_n_digits(new->limbs, new->ob_size, 0, new2->limbs, new2->ob_size); Py_DECREF(new); return new2; } /* Maybe all the lost digits are 0. */ for (i = self->ob_size - exp_to_i(expdiff); i < self->ob_size; i++) { if(_limb_get_digit(self->limbs, self->ob_size, i) > 0) goto no_way; } /* All lost digits are 0, so just clobber new */ while(new->ob_size > prec) { _limb_cut_one_digit(new->limbs, new->ob_size); /* VERY SLOW */ new->ob_size --; } new->limb_count = (new->ob_size + LOG - 1)/LOG; /* new->exp += expdiff; */ exp_inp_add(&(new->exp), expdiff); if (handle_Rounded(ctx, NULL) != 0) { Py_DECREF(new); return NULL; } return new; 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; }*/ /* 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); Py_DECREF(new); if (!new2) goto error; if (handle_Rounded(ctx, NULL) != 0) goto error; if (handle_Inexact(ctx, "Changed in rounding") != 0) goto error; Py_XDECREF(ctx2); return new2; error: Py_XDECREF(ctx2); Py_XDECREF(new2); return NULL; } /* Default values: rounding=-1 (use context), watchexp=1 */ static decimalobject * _decimal_rescale(decimalobject *self, exp_t exp, contextobject *ctx, int rounding, int watchexp) { decimalobject *ans = NULL, *tmp; int ret; exp_t diff; exp_t adj; long digits, i; if (ISSPECIAL(self)) { if (ISINF(self)) return handle_InvalidOperation(self->ob_type, ctx, "rescale with an INF", NULL); ret = _check_nans(self, NULL, ctx, &ans); if (ret != 0) /* ans is NULL in case of an error */ return ans; } if (watchexp && (exp_g(exp, ctx->Emax) || exp_l(exp, ETINY(ctx)))) return handle_InvalidOperation(self->ob_type, ctx, "rescale(a, INF)", NULL); if (!decimal_nonzero(self)) { ans = _NEW_decimalobj(1, self->sign, exp); if (!ans) return NULL; ans->limbs[0] = 0; return ans; } diff = exp_sub(self->exp, exp); digits = exp_to_i(exp_add_i(diff, self->ob_size)); if (watchexp && ctx->prec < digits) return handle_InvalidOperation(self->ob_type, ctx, "Rescale > prec", NULL); digits += 1; if (digits < 0) { ans = _NEW_decimalobj(2, self->sign, exp_sub_i(self->exp, digits)); if (!ans) return NULL; ans->limbs[0] = 1; digits = 1; } else { ans = _NEW_decimalobj(self->ob_size+1, self->sign, self->exp); if (!ans) return NULL; ans->limbs[ans->limb_count-1] = 0; for (i=0 ; i < self->limb_count;i++) ans->limbs[i] = self->limbs[i]; } /* if watchexp, we need to check if after rounding ob_size is still <= prec * there might be faster and easier solution I cannot see */ if (watchexp && digits > ctx->prec) { PyObject *flags; PyObject *r; flags = context_ignore_all_flags(ctx); if (!flags) { Py_DECREF(ans); return NULL; } tmp = _decimal_round(ans, digits, ctx, rounding); if (!tmp) { r = context_regard_flags(ctx, flags); Py_DECREF(flags); Py_XDECREF(r); Py_DECREF(ans); return NULL; } r = context_regard_flags(ctx, flags); Py_DECREF(flags); if (!r) { Py_DECREF(ans); Py_DECREF(tmp); return NULL; } Py_DECREF(r); if (_limb_get_digit(tmp->limbs, tmp->ob_size, 0) == 0 && tmp->ob_size > 1) tmp->ob_size --; if (tmp->ob_size > ctx->prec) { Py_DECREF(tmp); Py_DECREF(ans); return handle_InvalidOperation(self->ob_type, ctx, "Rescale > prec", NULL); } Py_DECREF(tmp); } tmp = _decimal_round(ans, digits, ctx, rounding); Py_DECREF(ans); if (!tmp) return NULL; if(_limb_get_digit(tmp -> limbs, tmp->ob_size, 0) == 0 && tmp->ob_size > 1){ /* We need one digit less, just clobber tmp. */ tmp->ob_size--; tmp->limb_count = (tmp->ob_size + LOG -1)/LOG; } tmp->exp = exp; adj = ADJUSTED(tmp); if (decimal_nonzero(tmp)) { if (exp_l(adj, ctx->Emin)) { if (handle_Subnormal(ctx, NULL) != 0) return NULL; } else if (exp_g(adj, ctx->Emax)) { ans = handle_InvalidOperation(self->ob_type, ctx, "rescale(a, INF)", NULL); Py_DECREF(tmp); return ans; } } return tmp; } static PyObject * decimal_rescale(decimalobject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"exp", "rounding", "context", "watchexp", 0}; contextobject *ctx = NULL; exp_t exp; PyObject *tmp_exp; int rounding = -1, watchexp = 1; if(!PyArg_ParseTupleAndKeywords(args,kwds,"O|iOi:_rescale",kwlist, &tmp_exp, &rounding, &ctx, &watchexp)) return NULL; exp = exp_from_pyobj(tmp_exp); if (PyErr_Occurred()) return NULL; if(ctx == NULL) if(!(ctx = getcontext())) return NULL; if(!PyDecimalContext_Check(ctx)){ PyErr_SetString(PyExc_TypeError, "context must be Context object"); return NULL; } return (PyObject*)_decimal_rescale(self,exp,ctx,rounding,watchexp); } /* Fix the exponents and return a copy with the exponents in bounds. * Only call if known not to be a special value. */ static decimalobject * _fixexponents(decimalobject *self, contextobject *ctx) { decimalobject *ans = NULL; exp_t adj; assert(!ISSPECIAL(self)); adj = ADJUSTED(self); if (exp_l(adj, ctx->Emin)) { exp_t Etiny = ETINY(ctx); if (exp_l(self->exp, Etiny)) { if (!decimal_nonzero(self)) { ans = _decimal_get_copy(self); if (!ans) return NULL; ans->exp = Etiny; if (handle_Clamped(ctx, NULL) != 0) goto err; return ans; } ans = _decimal_rescale(self, Etiny, ctx, -1, 1); if (!ans) return NULL; if (!decimal_nonzero(ans)) { if (handle_Clamped(ctx, NULL) != 0) goto err; } if (handle_Subnormal(ctx, NULL) != 0) goto err; /* TODO actually, it needs fixing */ if (_is_flag_set(ctx->flags, S_INEXACT)) { if (handle_Underflow(ctx, NULL) != 0) goto err; } return ans; } else { if (decimal_nonzero(self)) { if (handle_Subnormal(ctx, NULL) != 0) return NULL; } /* this returns self, below */ } } else { exp_t Etop = ETOP(ctx); if (ctx->clamp && exp_g(self->exp, Etop)) { if (handle_Clamped(ctx, NULL) != 0) return NULL; ans = _decimal_rescale(self, Etop, ctx, -1, 1); if (!ans) return NULL; return ans; } else { if (exp_g(adj, ctx->Emax)) { if (!decimal_nonzero(self)) { ans = _decimal_get_copy(self); if (!ans) return NULL; ans->exp = ctx->Emax; if (handle_Clamped(ctx, NULL) != 0) goto err; return ans; } if (handle_Inexact(ctx, NULL) != 0) return NULL; if (handle_Rounded(ctx, NULL) != 0) return NULL; return handle_Overflow(self->ob_type, ctx, "above Emax", self->sign); } } } Py_INCREF(self); return self; err: Py_XDECREF(ans); return NULL; } static decimalobject * _decimal_fix_nan(decimalobject *self, contextobject *ctx) { decimalobject *ans; 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 */ 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; } ans = _fixexponents(self, ctx); if (!ans) return NULL; if (ans->ob_size > ctx->prec) { decimalobject *rounded; rounded = _decimal_round(ans, ctx->prec, ctx, -1); Py_DECREF(ans); if (!rounded) return NULL; ans = _fixexponents(rounded, ctx); Py_DECREF(rounded); if (!ans) return NULL; } return ans; } /* this one cuts off diagnostic information */ static PyObject * decimal_fix(decimalobject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"context",0}; contextobject *ctx; if(!PyArg_ParseTupleAndKeywords(args, kwds, "O:_fix", kwlist, &ctx)) return NULL; if((PyObject*)ctx == Py_None) if(!(ctx = getcontext())) return NULL; return (PyObject*)_decimal_fix(self,ctx); } /* convert something to a Decimal. On failure, either returns NotImplemented or raises an exception, depending on the raise argument. */ static PyObject * _convert_to_decimal(PyTypeObject *type, PyObject *thing, contextobject *ctx, int raise) { if (PyDecimal_Check(thing)) { Py_INCREF(thing); return thing; } else if (PyInt_Check(thing)) { long val = PyInt_AsLong(thing); if (val == -1 && PyErr_Occurred()) return NULL; return decimal_from_long(type, val); } else if (PyLong_Check(thing)) { return (PyObject *)_decimal_from_pylong(type, thing, ctx); } /* failed to convert */ if (raise) { PyErr_SetString(PyExc_TypeError, "Cannot convert to a Decimal"); return NULL; } Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } /* Often-used context lists. */ static char *ctxkwlist[] = {"context", 0}; static char *decctxkwlist[] = {"other", "context", 0}; /* Make sure that dec is a decimal. Needs to have a contextobject named "ctx" around. */ #define ENSURE_DECIMAL(methname, dec, type) \ dec = (decimalobject *)_convert_to_decimal(type, (PyObject *)dec, ctx, 1); \ if (!dec) { \ PyErr_SetString(PyExc_TypeError, methname ": " #dec " must be a Decimal object"); \ return NULL; \ } /* Make sure that ctx is a valid context. This is commonly used after a PyArg_Parse... which might leave ctx NULL. */ #define ENSURE_CONTEXT(methname, ctx) \ if (ctx == NULL) { \ if (!(ctx = getcontext())) return NULL; \ } else if (!PyDecimalContext_Check(ctx)) { \ PyErr_SetString(PyExc_TypeError, methname ": context must be a Context object"); \ return NULL; \ } /* Define some wrappers that parse Python arguments and call the respective C functions. Unfortunately, that's not possible for methods that have a strange signature. XXX: should the _do_... functions be declared inline? (there actually is a new macro called Py_LOCAL for this). */ /* Define a Decimal method with one argument (the context). The _do_* version is then called with a valid context. */ #define DECIMAL_UNARY_FUNC(methname) \ static PyObject * \ decimal_##methname(decimalobject *self, PyObject *args, PyObject *kwds) \ { \ contextobject *ctx = NULL; \ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:" #methname, ctxkwlist, &ctx)) \ return NULL; \ ENSURE_CONTEXT(#methname, ctx); \ return (PyObject *)_do_decimal_##methname(self, ctx); \ } /* Define a Decimal method with two arguments (another decimal which might also be an integer and a context). The _do_* version is then called with an actual decimalobject and the context. */ #define DECIMAL_BINARY_FUNC(methname) \ static PyObject * \ decimal_##methname(decimalobject *self, PyObject *args, PyObject *kwds) \ { \ PyObject *res; \ decimalobject *dec = NULL; \ contextobject *ctx = NULL; \ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:" #methname, \ decctxkwlist, &dec, &ctx)) \ return NULL; \ ENSURE_CONTEXT(#methname, ctx); \ ENSURE_DECIMAL(#methname, dec, self->ob_type); \ res = (PyObject *)_do_decimal_##methname(self, dec, ctx); \ Py_DECREF(dec); \ return res; \ } /* Returns -1, 0, 1. No special error return code, must check for that with PyErr_Occurred(). */ static int _do_real_decimal_compare(decimalobject *self, decimalobject *other, contextobject *ctx) { exp_t adj1, adj2; long i, minsize; decimalobject *longer, *ans; contextobject *ctx2; PyObject *flags; if (ISSPECIAL(self) || ISSPECIAL(other)) { decimalobject *nan; if (_check_nans(self, other, ctx, &nan) != 0) { Py_XDECREF(nan); /* comparisons with NaN always report self>other */ return 1; } if(ISINF(self) || ISINF(other)) { if(ISINF(self) == ISINF(other)) return 0; else return ISINF(self) > ISINF(other) ? 1 : -1 ; } } /* If both are 0, sign comparison isn't correct. */ if (!decimal_nonzero(self) && !decimal_nonzero(other)) return 0; /* If different signs, neg one is less */ if ((other->sign & 1) && !(self->sign & 1)) return 1; if ((self->sign & 1) && !(other->sign & 1)) return -1; adj1 = ADJUSTED(self); adj2 = ADJUSTED(other); if (exp_eq(adj1, adj2)) { if (self->ob_size <= other->ob_size) { minsize = self->ob_size; longer = other; } else { minsize = other->ob_size; longer = self; } for (i = 0; i < minsize; i++) { if (_limb_get_digit(self->limbs, self->ob_size, i) != /* SLOW */ _limb_get_digit(other->limbs, other->ob_size, i)) goto next; } /* Okay, now the remaining digits of the longer one must consist of only digits. */ for (i = minsize; i < longer->ob_size; i++) /* SLOW */ if (_limb_get_digit(longer->limbs, longer->ob_size, i) != 0) goto next; /* All digits match, so they're equal. */ return 0; } next: /* Adjusted sizes are not equal. */ if(exp_g(adj1, adj2) && _limb_get_digit(self->limbs, self->ob_size, 0) != 0) return 1 - 2*(self->sign & 1); /* 0 -> 1, 1 -> -1 */ else if(exp_l(adj1, adj2) && _limb_get_digit(other->limbs, other->ob_size, 0) != 0) return -1 + 2*(other->sign & 1); ctx2 = context_copy(ctx); if (!ctx2) return 0; /* error */ ctx2->rounding = ROUND_UP; /* away from 0 */ if ((flags = context_ignore_all_flags(ctx2)) == NULL) return 0; /* error */ ans = _do_decimal_subtract(self, other, ctx2); Py_DECREF(ctx2); Py_DECREF(flags); if (!ans) return 0; if (!decimal_nonzero(ans)) i = 0; else if (ans->sign & 1) i = -1; else i = 1; Py_DECREF(ans); return i; } static decimalobject * _do_decimal_compare(decimalobject *self, decimalobject *other, contextobject *ctx) { int res; if (ISSPECIAL(self) || (decimal_nonzero(other) && ISSPECIAL(other))) { decimalobject *nan; if (_check_nans(self, other, ctx, &nan) != 0) return nan; } res = _do_real_decimal_compare(self, other, ctx); return (decimalobject *)decimal_from_long(self->ob_type, res); } DECIMAL_BINARY_FUNC(compare) static int decimal_cmp(decimalobject *self, decimalobject *other) { contextobject *ctx; int res; ctx = getcontext(); if (!ctx) return 0; other = (decimalobject *)_convert_to_decimal(self->ob_type, (PyObject*)other, ctx, 1); if (!other) return 0; /* XXX ??*/ if ((PyObject*) other == Py_NotImplemented) return 0; res = _do_real_decimal_compare(self, other, ctx); Py_DECREF(other); /* we will return 0 anyway */ /* if (PyErr_Occurred()) return NULL; */ return res; } static PyObject * decimal_richcompare(decimalobject *self, decimalobject *other, int op) { int res; contextobject *ctx; ctx = getcontext(); if (!ctx) return NULL; other = (decimalobject *)_convert_to_decimal(self->ob_type, (PyObject *)other, ctx, 0); if (!other) return NULL; if ((PyObject*)other == Py_NotImplemented) return (PyObject*)other; res = _do_real_decimal_compare(self, other, ctx); Py_DECREF(other); if (PyErr_Occurred()) return NULL; if ( (res == 0 && (op == Py_EQ || op == Py_LE || op == Py_GE)) || (res == -1 && (op == Py_NE || op == Py_LT || op == Py_LE)) || (res == 1 && (op == Py_NE || op == Py_GT || op == Py_GE))) Py_RETURN_TRUE; else Py_RETURN_FALSE; } static decimalobject * _do_decimal_max(decimalobject *self, decimalobject *other, contextobject *ctx) { decimalobject *ans; int cmp; contextobject *ctx2; if (ISSPECIAL(self) || ISSPECIAL(other)) { decimalobject *nan; int sn = GETNAN(self); int so = GETNAN(other); if (sn || so) { if (so == 1 && sn != 2) { Py_INCREF(self); return self; } else if (sn == 1 && so != 2) { Py_INCREF(other); return other; } } if (_check_nans(self, other, ctx, &nan) != 0) return nan; } ans = self; /* let's assume */ if(!ctx) { ctx = getcontext(); } /* we have to give getcontext() to _do_decimal_compare */ ctx2 = getcontext(); if(!ctx2) return NULL; cmp = _do_real_decimal_compare(self, other,ctx2); if(PyErr_Occurred()) return NULL; if(!cmp) { if(self->sign != other->sign) { if(self->sign) ans = other; } else { if (exp_l(self->exp, other->exp) && !self->sign) ans = other; else if (exp_g(self->exp, other->exp) && self->sign) ans = other; } } else if(cmp == -1) ans = other; if(ctx ->rounding_dec == ALWAYS_ROUND) return _decimal_fix(ans, ctx); Py_INCREF(ans); return ans; } DECIMAL_BINARY_FUNC(max) static decimalobject * _do_decimal_min(decimalobject *self, decimalobject *other, contextobject *ctx) { decimalobject *ans; int cmp; contextobject *ctx2; if(ISSPECIAL(self) || ISSPECIAL(other)) { decimalobject *nan; int sn = GETNAN(self); int so = GETNAN(other); if(sn || so) { if(so == 1 && sn != 2) { Py_INCREF(self); return self; } else if (sn == 1 && so != 2) { Py_INCREF(other); return other; } } if(_check_nans(self, other, ctx, &nan) != 0) return nan; } ans = self; if(!ctx) ctx = getcontext(); if(!ctx) return NULL; ctx2 = getcontext(); if(!ctx2) return NULL; cmp = _do_real_decimal_compare(self,other,ctx2); if(PyErr_Occurred()) return NULL; if(!cmp) { if(self->sign != other->sign) { if(!self->sign) ans = other; } else{ if (exp_g(self->exp, other->exp) && !self->sign) ans = other; else if (exp_l(self->exp, other->exp) && self->sign) ans = other; } } else if(cmp == 1) ans = other; if(ctx->rounding_dec == ALWAYS_ROUND) return _decimal_fix(ans, ctx); Py_INCREF(ans); return ans; } DECIMAL_BINARY_FUNC(min) /* strip trailing 0s, change anything equal to 0 to 0e0 */ static decimalobject * _do_decimal_normalize(decimalobject *self, contextobject *ctx) { decimalobject *dup, *new; if (ISSPECIAL(self)) { decimalobject *nan = NULL; int res; res = _check_nans(self, NULL, ctx, &nan); if (res != 0) return nan; /* can be NULL on error */ } dup = _decimal_fix(self, ctx); if (!dup) return NULL; if (ISINF(dup)) return dup; if (!decimal_nonzero(dup)) { new = _NEW_decimalobj(1, dup->sign, exp_from_i(0)); Py_DECREF(dup); if (!new) return NULL; new->limbs[0] = 0; return new; } /* strip trailing 0s from dup */ /* SLOW */ while(_limb_get_digit(dup->limbs, dup->ob_size, dup->ob_size -1) == 0){ _limb_cut_one_digit(dup->limbs,dup->ob_size); dup->ob_size --; exp_inc(&(dup->exp)); } return dup; } DECIMAL_UNARY_FUNC(normalize) /* strip trailing 0s that are right to the dot */ static decimalobject * _do_decimal_trim(decimalobject *self, contextobject *ctx) { decimalobject *new; if (ISSPECIAL(self)) { Py_INCREF(self); return self; } if (!decimal_nonzero(self)) { new = _NEW_decimalobj(1, self->sign, exp_from_i(0)); if (!new) return NULL; new->limbs[0] = 0; return new; } new = _decimal_get_copy(self); if (!new) return NULL; /* SLOW */ if (exp_le_i(new->exp, 0)) while (_limb_get_digit(new->limbs, new->ob_size, new->ob_size - 1) == 0 && exp_l_i(new->exp, 0)) { _limb_cut_one_digit(new->limbs, new->ob_size); new->ob_size --; exp_inc(&(new->exp)); } else while (_limb_get_digit(new->limbs, new->ob_size, new->ob_size - 1) == 0) { _limb_cut_one_digit(new->limbs, new->ob_size); new->ob_size --; exp_inc(&(new->exp)); } return new; } DECIMAL_UNARY_FUNC(trim) /* Quantize self so that its exponent is the same as the exponent of another. */ static decimalobject * _do_decimal_quantize(decimalobject *self, decimalobject *other, contextobject *ctx, int rounding, int watchexp) { 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 handle_InvalidOperation(self->ob_type, ctx, "quantize with one INF", NULL); } } return _decimal_rescale(self, other->exp, ctx, rounding, watchexp); } 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; } return _do_decimal_quantize(self, other, ctx, rounding, watchexp); } /* it is rewritten from python implementation */ static decimalobject * _do_decimal_remainder_near(decimalobject *self, decimalobject *other, contextobject *ctx) { contextobject *ctx2; decimalobject *side, *r; decimalobject *comparison = 0; int rounding_dec; int decrease; PyObject *ignored_flags; PyObject *flags = 0; int s1, s2; if (ISSPECIAL(self) || ISSPECIAL(other)) { decimalobject *nan; if (_check_nans(self, other, ctx, &nan)) return nan; } if (decimal_nonzero(self) && !decimal_nonzero(other)) return handle_InvalidOperation(self->ob_type, ctx, "x % 0", NULL); ctx2 = context_shallow_copy(ctx); if (!ctx2) return NULL; ignored_flags = PyTuple_New(2); if (!ignored_flags) goto err; Py_INCREF(errors[S_ROUNDED]); Py_INCREF(errors[S_INEXACT]); PyTuple_SET_ITEM(ignored_flags, 0, errors[S_ROUNDED]); PyTuple_SET_ITEM(ignored_flags, 1, errors[S_INEXACT]); flags = context_ignore_flags(ctx2, ignored_flags); Py_DECREF(ignored_flags); if (!flags) goto err; { PyObject *divmod = _do_decimal__divide(self, other, 1, ctx2); if (!divmod) { PyObject *r = context_regard_flags(ctx2, flags); Py_XDECREF(r); goto err; } side = (decimalobject*) PySequence_GetItem(divmod, 0); r = (decimalobject*) PySequence_GetItem(divmod, 1); Py_DECREF(divmod); } if (GETNAN(r)) { PyObject *ret; ret = context_regard_flags(ctx2, flags); if (!ret) goto err; Py_DECREF(ret); Py_DECREF(flags); Py_DECREF(ctx2); Py_DECREF(side); return r; } rounding_dec = ctx2->rounding_dec; ctx2->rounding_dec = NEVER_ROUND; { decimalobject *two = _NEW_decimalobj(1, 0, exp_from_i(0)); if (!two) { PyObject *r = context_regard_flags(ctx2, flags); Py_XDECREF(r); goto err; } two->limbs[0] = 2; two->sign = other->sign&1; comparison = (decimalobject*) _do_decimal__divide(other, two, 0, ctx2); Py_DECREF(two); if (!comparison) goto err; } ctx2->rounding_dec = rounding_dec; { PyObject *ret; ret = context_regard_flags(ctx2, flags); if (!ret) goto err; Py_DECREF(ret); } s1 = r->sign; s2 = comparison->sign; /* we don't want to loose info about infinity */ r->sign &= 254; comparison->sign &= 254; { int cmp = _do_real_decimal_compare(r, comparison, ctx2); if (PyErr_Occurred()) goto err; if (cmp == -1) { PyObject *ret; decimalobject *fixed; r->sign = s1; comparison->sign = s2; ret = _do_decimal__divide(self, other, 1, ctx2); if (!ret) goto err; Py_DECREF(ret); fixed = _decimal_fix(r, ctx2); Py_DECREF(r); Py_DECREF(ctx2); Py_DECREF(flags); Py_DECREF(side); Py_DECREF(comparison); return fixed; } } r->sign = s1; comparison->sign = s2; rounding_dec = ctx2->rounding_dec; ctx2->rounding_dec = NEVER_ROUND; { PyObject *divmod = _do_decimal__divide(self, other, 1, ctx2); if (!divmod) goto err; Py_DECREF(side); Py_DECREF(r); side = (decimalobject *)PySequence_GetItem(divmod, 0); r = (decimalobject *)PySequence_GetItem(divmod, 1); Py_DECREF(divmod); } ctx2->rounding_dec = rounding_dec; if (GETNAN(r)) { Py_DECREF(ctx2); Py_DECREF(flags); Py_DECREF(side); Py_DECREF(comparison); return r; } decrease = side->limbs[0] &1; side->sign &= 254; s1 = r->sign; s2 = comparison->sign; r->sign &= 254; comparison->sign &= 254; { int cmp = _do_real_decimal_compare(r, comparison, ctx2); if (PyErr_Occurred()) goto err; if ((cmp == 1) || (decrease && cmp == 0)) { r->sign = s1; comparison->sign = s2; ctx2->prec += 1; { decimalobject *one; decimalobject *tmp; one = _NEW_decimalobj(1,0,exp_from_i(0)); if (!one) goto err; one->limbs[0] = 1; tmp = _do_decimal_add(side, one, ctx2); Py_DECREF(one); if (!tmp) goto err; if (tmp->ob_size >= ctx2->prec) { PyObject *ret; PyObject *tup; Py_DECREF(tmp); ctx2->prec -= 1; tup = handle_DivisionImpossible(self->ob_type, ctx2, NULL); if (!tup) goto err; ret = PySequence_GetItem(tup, 1); Py_DECREF(tup); Py_DECREF(ctx2); Py_XDECREF(flags); Py_DECREF(side); Py_DECREF(r); Py_DECREF(comparison); return (decimalobject *)ret; } Py_DECREF(tmp); } ctx2->prec -= 1; if (self->sign == other->sign) { decimalobject *tmp = _do_decimal_subtract(r, other, ctx2); if (!tmp) goto err; Py_DECREF(r); r = tmp; tmp = 0; } else { decimalobject *tmp = _do_decimal_add(r, other, ctx2); if (!tmp) goto err; Py_DECREF(r); r = tmp; } } else { r->sign = s1; comparison->sign = s2; } } Py_DECREF(comparison); Py_DECREF(side); Py_DECREF(flags); Py_DECREF(ctx2); { decimalobject *fixed = _decimal_fix(r, ctx); if (!fixed) { Py_DECREF(r); return NULL; } Py_DECREF(r); return fixed; } err: Py_DECREF(ctx2); Py_XDECREF(flags); Py_XDECREF(side); Py_XDECREF(r); Py_XDECREF(comparison); return NULL; } DECIMAL_BINARY_FUNC(remainder_near) static PyObject * _do_decimal_same_quantum(decimalobject *self, decimalobject *other) { 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 (exp_eq(self->exp, other->exp)) Py_RETURN_TRUE; else Py_RETURN_FALSE; } 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; } return _do_decimal_same_quantum(self, other); } /* this code is rewritten from python version, this might change * in future */ static decimalobject * _do_decimal_sqrt(decimalobject *self, contextobject *ctx) { decimalobject *ret = 0, *ret2 = 0; long *b_tab; /* our temporal storage for integer DD */ long b_limbs; exp_t e; /* as below */ int i; int exact = 1; /* is result exact */ int shift = 0; /* in case result is exact - we need to restore perfect exponent */ long ret_limbs; int rounding; if (ISSPECIAL(self)) { decimalobject *nan; if (_check_nans(self, NULL, ctx, &nan)) return nan; if (ISINF(self) && (self->sign&1) == 0) return _decimal_get_copy(self); } if (!decimal_nonzero(self)) { ret = _NEW_decimalobj(1, 0, exp_from_i(0)); if (!ret) return NULL; ret->limbs[0] = 0; ret->exp = exp_floordiv_i(self->exp, 2); ret->sign = self->sign & 1; return ret; } if (self->sign&1) { return handle_InvalidOperation(self->ob_type, ctx, "sqrt(-x), x > 0", NULL); } /* The idea is pretty much the same that in Python code. * Assume, that we have real a, computed with precision prec + 1 * that a <= sqrt(self) < a + 1ULP, and we know if a == sqrt(self) * (is it exact). With this a, we have two cases: * a) a == sqrt(self) -- just round a, and return * b) otherwise -- we know, that a < sqrt(self) < a + 1ULP * the only problem that arise, is when a ends with 5 or 0 (this is * the only case, that ROUND_HALF_EVEN will give incorrect result, * because if result ends with 5 or 5000..0, it's actually bigger) * so we just check last digit, if it's 5, we add 1ULP to a, round, * and return. * * So now we want to find a. We'll find such b and e, that * self = b * 10^e, e mod 2 == 0, e is integer and * floor(sqrt(b)) has at last prec+1 significant digits -- that is * c = floor(sqrt(b)) 10^(prec) <= c, so 10^(prec) <= sqrt(b), so * 10^(2*prec) <= b. Given b and e, a = floor(sqrt(floor(b)) * 10^(e/2) * we know that floor(sqrt(floor(b)) <= sqrt(b), so * a <= sqrt(b) * 10^(e/2) = self. On the other side * floor(sqrt(floor(b)) <= sqrt(b) < floor(sqrt(floor(b)) + 1 * [the proof is pretty straightforward, show that there is no x /in (i,i+1) * that sqrt(x) is integer for any integer i] * Concluding, self < (a+1) * 10^(e/2) = a * 10^(e/2) + 10^(e/2) = a * 10^(e/2) + 1ULP [] * Concluding (one more time), we get b with at least 2*prec + 1 digits * and we make sure, that e is even, self = b * 10^e; now, we truncate * fractional part b (let DD = floor(b)), and set a = floor(sqrt(DD)) * 10^(e/2) */ /* we need at last ctx->prec*2 + 1 digits, givent n limbs, * we have at last n*4-3 digits, n*4-3 >= ctx->prec*2 + 1 * n*4 >= ctx->prec * 2 + 4, n >= ctx->prec/2 + 1, so will * set n = ctx->prec/2 + 3 (because there might be possibility, that * we'll need to make exp even - in that case we'll extend b) * but we have to fill b_limbs - 1 limbs */ b_limbs = ctx->prec/2 + 3; b_tab = (long*)malloc(b_limbs * sizeof(long)); if(b_limbs -1 > self->limb_count) { /* we have to extend */ int diff = (b_limbs - 1) - self->limb_count; shift = -diff * 4; /*exp becomes smaller */ for(i = 0;i < self->limb_count; i++) b_tab[i+diff] = self->limbs[i]; for(i = 0;i < diff; i++) b_tab[i] = 0; } else { /* we have to truncate, we'll fill b_limbs-1 limbs */ int diff = self->limb_count - (b_limbs - 1); shift = diff * 4; /* we truncated diff limbs, so exp will become greater */ for(i = 0;i < b_limbs - 1; i++) b_tab[i] = self->limbs[i + diff]; /* we'll check remainding limbs of self, to check if * we truncated something important */ for(i = 0;i < diff; i++) if(self->limbs[i]) { exact = 0; break; } } b_tab[b_limbs-1] = 0; /* only one we didn't set */ /* in case exp mod 2 = 1, we'll extend multiply b_tab * 10, and decrease shift * [that's why we made room for another limb] */ if(exp_mod_i(self->exp, 2)) { shift --; _limb_add_one_digit(b_tab, (b_limbs-1) * LOG, 0); } b_limbs = _limb_size(b_tab, b_limbs); /* ok, now our DD is b_tab, and our e is self->exp + shift */ /* how big tab we need - sqrt uses ceil(limbs/2) + 1, but there * is possibility that we're going to set ideal exp * Our result exp will be (self->exp + shift)/2, and ideal exp is * floor(self->exp/2), so we might be forced to extend mantisa by * shift/2 */ ret_limbs = (b_limbs + 1)/2 + 2 + (shift > 0 ? (shift + 1)/2 + 1 : 0); /* TODO maybe I should use some temporary tab first? */ ret = _NEW_decimalobj(ret_limbs * LOG, self->sign, exp_from_i(0)); if(!ret) { free(b_tab); return NULL; } ret_limbs = _limb_sqrt(b_tab, b_limbs, ret->limbs); ret->limb_count = ret_limbs; ret->ob_size = _limb_size_s(ret->limbs, ret_limbs * LOG); /* let's check if it's exact */ { long tmp_digs; long cmp_res; long *tmp = (long*)malloc((ret_limbs * 2 + 1) * sizeof(long)); tmp_digs = _limb_multiply(ret->limbs, ret->ob_size, ret->limbs, ret->ob_size, tmp); cmp_res = _limb_compare_un(b_tab, b_limbs, tmp, (tmp_digs +LOG-1)/LOG); exact = exact && cmp_res == 0; free(tmp); } free(b_tab); /* now all we need is to set exp :> and add 1 ULP in case it's not exact ;> */ if (exact) { long expdiff; exp_t tmp_exp; ret->exp = exp_floordiv_i(self->exp, 2); tmp_exp = exp_floordiv_i(exp_add_i(self->exp, shift), 2); expdiff = exp_to_i(exp_sub(ret->exp, tmp_exp)); /* TODO SLOW */ if(expdiff > 0) { while(expdiff && ret->ob_size > 1) { assert(!(ret->limbs[0] % 10)); /* Am I sure?? TODO */ _limb_cut_one_digit(ret->limbs, ret->ob_size); ret->ob_size --; expdiff --; } } else { while(expdiff) { _limb_add_one_digit(ret->limbs, ret->ob_size, 0); ret->ob_size ++; expdiff ++; } } } else { if(ret->limbs[0] % 5 == 0) ret->limbs[0] ++; ret-> exp = exp_floordiv_i(exp_add_i(self->exp, shift), 2); } /* TODO it's not thread safe */ rounding = ctx->rounding; ctx->rounding = ROUND_HALF_EVEN; ret2 = _decimal_fix(ret, ctx); ctx->rounding = rounding; Py_DECREF(ret); return ret2; return NULL; } DECIMAL_UNARY_FUNC(sqrt) static PyObject * _do_decimal_to_eng_string(decimalobject *self, contextobject *ctx) { return _do_decimal_str(self, ctx, 1); } DECIMAL_UNARY_FUNC(to_eng_string) static decimalobject * _do_decimal_to_integral(decimalobject *self, contextobject *ctx, int rounding) { int rnd, inex; decimalobject *ans; if (ISSPECIAL(self)) { decimalobject *nan; if (_check_nans(self, NULL, ctx, &nan) != 0) return nan; } if (exp_is_pos(self->exp) || exp_is_zero(self->exp)) { Py_INCREF(self); return self; } /* XXX: slow? */ rnd = _set_flag(ctx->ignored, S_ROUNDED, 1); inex =_set_flag(ctx->ignored, S_INEXACT, 1); ans = _decimal_rescale(self, exp_from_i(0), ctx, rounding, 1); if (!rnd) _set_flag(ctx->ignored, S_ROUNDED, rnd); if (!inex) _set_flag(ctx->ignored, S_INEXACT, inex); return ans; } static PyObject * decimal_to_integral(decimalobject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"rounding", "context", 0}; contextobject *ctx = NULL; int rounding = -1; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO:to_integral", kwlist, &rounding, &ctx)) return NULL; ENSURE_CONTEXT("to_integral", ctx); if(rounding == -1) rounding = ctx->rounding; if (!VALID_ROUND(rounding)) { PyErr_SetString(PyExc_ValueError, "invalid rounding value"); return NULL; } return (PyObject *)_do_decimal_to_integral(self, ctx, rounding); } static PyObject * decimal_reduce(decimalobject *self) { PyObject *str, *val; str = decimal_str(self); if (!str) return NULL; val = Py_BuildValue("(O(O))", self->ob_type, str); Py_DECREF(str); return val; } /* Decimals are immutable, therefore copy() returns self. */ static PyObject * decimal_copy_method(PyObject *self) { Py_INCREF(self); return self; } /* Contents of Decimal are also immutable. */ static PyObject * decimal_deepcopy(PyObject *self, PyObject *memo) { Py_INCREF(self); return self; } /* For internal use; this does what Python code achieves with "Decimal(self)". */ static decimalobject * _decimal_get_copy(decimalobject *self) { decimalobject *new; long i; new = _NEW_decimalobj(self->ob_size, self->sign, self->exp); if (!new) return NULL; for (i = 0;i < new->limb_count;i++) new->limbs[i] = self->limbs[i]; return new; } static PyObject * decimal_adjusted(decimalobject *self) { if (ISSPECIAL(self)) return PyInt_FromLong(0); return exp_to_pyobj(ADJUSTED(self)); } static PyObject * decimal_as_tuple(decimalobject *self) { PyObject *digits, *res, *d, *inf; long i; digits = PyTuple_New(self->ob_size); if (!digits) return NULL; for (i = 0; i < self->ob_size; i++) { d = PyInt_FromLong(_limb_get_digit(self->limbs, self->ob_size, i)); /* SLOW */ if (!d) return NULL; PyTuple_SET_ITEM(digits, i, d); } if(!ISINF(self)) { PyObject *exp = exp_to_pyobj(self->exp); if (!exp) { Py_DECREF(digits); return NULL; } res = Py_BuildValue("(bOO)", self->sign % 2, digits, exp); Py_DECREF(exp); } else { inf = PyString_FromString("F"); if (!inf) { Py_DECREF(digits); return NULL; } res = Py_BuildValue("(bOO)", self->sign % 2, digits, inf); Py_DECREF(inf); } Py_DECREF(digits); return res; } /* Stringizing and formatting handling */ /* max length of output is len(str(max_long)) + len(str(max_long)) + len(".") + len("-") */ #define FORMAT_SIZE 50 #define SANITY_CHECK(x) assert((x) < end) static PyObject * decimal_special_str(decimalobject *d) { char outbuf[FORMAT_SIZE]; char *p, *end; int i; end = &outbuf[FORMAT_SIZE]; p = outbuf; if (d->sign & 1) *p++ = '-'; SANITY_CHECK(p); if (GETNAN(d)) { if (GETNAN(d) == 2) *p++ = 's'; SANITY_CHECK(p); p += sprintf(p, "%s", "NaN"); SANITY_CHECK(p); /* check for digits != {0, } */ 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, "%ld", _limb_get_digit(d->limbs, d->ob_size, i)); /* SLOW */ SANITY_CHECK(p); } } } else { /* infinity */ p += sprintf(p, "%s", "Infinity"); } *p++ = 0; SANITY_CHECK(p); return PyString_FromString(outbuf); } static PyObject * decimal_eng_zero_str(decimalobject *d, contextobject *context) { char outbuf[FORMAT_SIZE]; char *p, *end; int i; exp_t roundexp; end = &outbuf[FORMAT_SIZE-1]; p = outbuf; if (d->sign & 1) { *p++ = '-'; SANITY_CHECK(p); } if(exp_is_neg(d->exp) && exp_ge_i(d->exp, -6)) { i = 0; *p++ = '0'; *p++ = '.'; for (;i exp));i++) *p++ = '0'; SANITY_CHECK(p); } else { roundexp = d->exp; while(exp_mod_i(roundexp, 3)) exp_inc (&roundexp); if (exp_ne(roundexp, d->exp)) { *p++ = '0'; *p++ = '.'; for (i = 0; i < (exp_to_i(exp_sub(roundexp, d->exp))); i++) *p++ = '0'; SANITY_CHECK(p); } else { *p++ = '0'; } if (!exp_is_zero(roundexp)) { if (context->capitals) *p++ = 'E'; else *p++ = 'e'; SANITY_CHECK(p); if (exp_is_pos(roundexp)) *p++ = '+'; SANITY_CHECK(p); p += exp_sprintf(p, roundexp); SANITY_CHECK(p); } } *p++ = 0; p = &outbuf[0]; return PyString_FromString(p); } PyObject * PyDecimal_Str(PyObject *self, PyObject *args, PyObject *kwds) { return _do_decimal_str((decimalobject *)self, NULL, 0); } static PyObject * decimal_str(decimalobject *d) { return _do_decimal_str(d, NULL, 0); } static PyObject * _do_decimal_str(decimalobject *d, contextobject *context, int engineering) { char *outbuf; int buflen, i; exp_t dotplace; exp_t adjexp; int append_E = 0, append_adjexp = 0; long extra_zeros=0; char *p, *end; PyObject *ret; if (context == NULL) { context = getcontext(); if (!context) return NULL; } if (ISSPECIAL(d)) return decimal_special_str(d); if (engineering && !decimal_nonzero(d)) return decimal_eng_zero_str(d, context); buflen = FORMAT_SIZE + d->ob_size; outbuf = (char *)PyMem_MALLOC(buflen); if (outbuf == NULL) return NULL; end = outbuf + buflen; p = &outbuf[1]; /* reserve the first byte for a possible leading zero */ if (d->sign & 1) { *p++ = '-'; } /* The rule is as follows, imagine you write integer: * 123456789 we want to print it, without adding post-zeros * or adding at most 6 pre-zeros that is * 0.00000123456789, if none of this can be done, then, we put dot * after first digit, and compute E *or* if engineering, then, we put * dot after 1st 2nd or 3rd digit, and assure that E is divisible by 3 * huh */ /* (1) dotplace = -adjexp + d->exp + d->ob_size */ /* why is that like? well, d->exp moves dot right, d->ob_size moves dot right * and adjexp moves dot left */ adjexp = exp_from_i(0); dotplace = exp_add_i(d->exp, d->ob_size); /* dotplace must be at most d->ob_size (no dot at all) and at last -5 (6 pre-zeros)*/ if (exp_g_i(dotplace, d->ob_size) || exp_l_i(dotplace, -5)) { /* ok, we have to put dot after 1 digit, that is dotplace = 1 * we compute adjexp from equation (1) */ dotplace = exp_from_i(1); adjexp = exp_add_i(exp_sub(d->exp,dotplace), d->ob_size); } if(engineering) /* we have to be sure, adjexp%3 == 0 */ while(exp_mod_i(adjexp,3)) { exp_dec(&adjexp); exp_inc(&dotplace); } /* now all we have to do, is to put it to the string =] */ if (exp_g_i(dotplace, d->ob_size)) extra_zeros = exp_to_i(exp_sub_i(dotplace, d->ob_size)); if (exp_le_i(dotplace, 0)) { *p++ = '0'; *p++ = '.'; while (exp_l_i(dotplace, 0)) { exp_inc(&dotplace); *p++ = '0'; } dotplace = exp_from_i(-1); } for(i = 0;i ob_size;i++) { if(exp_eq_i(dotplace, i)) *p++ = '.'; p += sprintf(p, "%ld", _limb_get_digit(d->limbs,d->ob_size,i)); } while(extra_zeros --)*p++ = '0'; /* that was a way easier way =] */ if(!exp_is_zero(adjexp)) { append_E = 1; append_adjexp = 1; } if (append_E) { if (context->capitals) { *p++ = 'E'; SANITY_CHECK(p); if (exp_is_pos(adjexp)) { *p++ = '+'; SANITY_CHECK(p); } } else { *p++ = 'e'; SANITY_CHECK(p); } } if (append_adjexp) { p += exp_sprintf(p, adjexp); } SANITY_CHECK(p); *p++ = 0; SANITY_CHECK(p); p = &outbuf[1]; SANITY_CHECK(p); ret = PyString_FromString(p); PyMem_FREE(outbuf); return ret; } #undef SANITY_CHECK static PyObject * decimal_XXX_round(decimalobject *self, PyObject *args) { contextobject *ctx = NULL; long prec = -1, rnd = -1; if (!PyArg_ParseTuple(args, "|llO", &prec, &rnd, &ctx)) return NULL; ENSURE_CONTEXT("_round", ctx); if (prec == -1) prec = ctx->prec; if (rnd == -1) rnd = ctx->rounding; return (PyObject*)_decimal_round(self, prec, ctx, rnd); } static PyObject * decimal_XXX_abs(decimalobject *self, PyObject *args) { int round = -1; contextobject *ctx; if (!PyArg_ParseTuple(args, "|iO", &round, &ctx)) return NULL; ENSURE_CONTEXT("abs", ctx); if (round == -1) round = ctx->rounding; return (PyObject*)_do_decimal_absolute(self, ctx, round); } static PyObject * _do_decimal__divide(decimalobject *self, decimalobject *other, int divmod, contextobject *ctx) { int sign = (self->sign&1) ^ (other->sign&1); int self_is_zero; int other_is_zero; int shouldround; decimalobject *op1, *op2; PyObject *ans; if (ISSPECIAL(self) || ISSPECIAL(other)) { decimalobject *nan; if (_check_nans(self, other, ctx, &nan)) { if (divmod) { ans = Py_BuildValue("(OO)", nan, nan); Py_XDECREF(nan); } else ans = (PyObject*) nan; return ans; } if (ISINF(self) && ISINF(other)) { if (divmod) { decimalobject *ans1, *ans2; ans1 = handle_InvalidOperation(self->ob_type, ctx, "(+-) INF // (+-) INF", NULL); if (!ans1) return NULL; ans2 = handle_InvalidOperation(self->ob_type, ctx, "(+-) INF % (+-) INF", NULL); if (!ans2) { Py_DECREF(ans1); return NULL; } ans = Py_BuildValue("(OO)", ans1, ans2); Py_DECREF(ans1); Py_DECREF(ans2); } else { ans = (PyObject*)handle_InvalidOperation(self->ob_type, ctx, "(+- INF / (+-) INF", NULL); } return ans; } if (ISINF(self)) { decimalobject *inf; int inf_sign; inf_sign = sign ? SIGN_NEGINF : SIGN_POSINF; inf = _NEW_decimalobj(1, inf_sign, exp_from_i(0)); if (!inf) return NULL; inf->limbs[0] = 0; if (divmod == 1 || divmod == 3) { decimalobject *ans2; ans2 = handle_InvalidOperation(self->ob_type, ctx, "INF % x", NULL); if (!ans2) { Py_DECREF(inf); return NULL; } ans = Py_BuildValue("(OO)", inf, ans2); Py_DECREF(inf); Py_DECREF(ans2); } else if (divmod == 2) { decimalobject *nan; nan = _NEW_decimalobj(1, SIGN_POSNAN, exp_from_i(0)); if (!nan) { Py_DECREF(inf); return NULL; } nan->limbs[0] = 0; ans = Py_BuildValue("(OO)", inf, nan); Py_DECREF(inf); Py_DECREF(nan); } /* divmod == 0 */ else { ans = (PyObject*) inf; } return ans; } if (ISINF(other)) { if (divmod) { decimalobject *ans1, *ans2; ans1 = _NEW_decimalobj(1, sign, exp_from_i(0)); if (!ans1) return NULL; ans1->limbs[0] = 0; ans2 = _decimal_get_copy(self); if (!ans2) { Py_DECREF(ans1); return NULL; } ans = Py_BuildValue("(OO)", ans1, ans2); Py_DECREF(ans1); Py_DECREF(ans2); } else { decimalobject *ret; if (handle_Clamped(ctx, NULL)) return NULL; ret = _NEW_decimalobj(1, sign, ETINY(ctx)); if (!ret) return NULL; ret->limbs[0] = 0; ans = (PyObject *) ret; } return ans; } } self_is_zero = !decimal_nonzero(self); other_is_zero = !decimal_nonzero(other); if (self_is_zero && other_is_zero) { if (divmod) { return (PyObject*)handle_DivisionUndefined(self->ob_type, ctx, "0 / 0", 1); } return (PyObject*)handle_DivisionUndefined(self->ob_type, ctx, "0 / 0", 0); } if (self_is_zero) { exp_t exp; if (divmod) { decimalobject *ans1, *ans2; ans2 = _decimal_get_copy(self); if (!ans2) return NULL; ans2->exp = exp_min(self->exp, other->exp); ans1 = _NEW_decimalobj(1, sign, exp_from_i(0)); if (!ans1) { Py_DECREF(ans2); return NULL; } ans1->limbs[0] = 0; ans = Py_BuildValue("(OO)", ans1, ans2); Py_DECREF(ans1); Py_DECREF(ans2); return ans; } exp = exp_sub(self->exp, other->exp); if (exp_l(exp, ETINY(ctx))) { exp = ETINY(ctx); if (handle_Clamped(ctx, "0e-x / y")) return NULL; } if (exp_g(exp, ctx->Emax)) { exp = ctx->Emax; if (handle_Clamped(ctx, "0e+x / y")) return NULL; } { decimalobject *ret; ret = _NEW_decimalobj(1, sign, exp); if (!ret) return NULL; ret->limbs[0] = 0; ans = (PyObject*) ret; return ans; } } if (other_is_zero) { if (divmod) { return (PyObject*)handle_DivisionByZero(self->ob_type, ctx, "divmod(x,0)", sign, 1); } return (PyObject*)handle_DivisionByZero(self->ob_type, ctx, "x / 0", sign, 0); } /* now fun starts, answer isnt one of 0 NaN or INF */ shouldround = ctx->rounding_dec == ALWAYS_ROUND; /* if we divide into ints, we'll check if self < other */ if (divmod) { decimalobject *abs1, *abs2; int cmp; abs1 = _do_decimal_absolute(self, ctx, 0); if (!abs1) return NULL; abs2 = _do_decimal_absolute(other, ctx, 0); if (!abs2) { Py_DECREF(abs1); return NULL; } cmp = _do_real_decimal_compare(abs1, abs2, ctx); Py_DECREF(abs1); Py_DECREF(abs2); if (PyErr_Occurred()) { return NULL; } if (cmp == -1) { decimalobject *ans1, *ans2; ans1 = _NEW_decimalobj(1, sign, exp_from_i(0)); if (!ans1) return NULL; ans1->limbs[0] = 0; if (divmod == 1 || divmod == 3) { decimalobject *tmp; exp_t exp; exp = exp_min(self->exp, other->exp); tmp = _decimal_rescale(self, exp, ctx, -1, 0); if (!tmp) { Py_DECREF(ans1); return NULL; } if (shouldround) { ans2 = _decimal_fix(tmp, ctx); if (!ans2) { Py_DECREF(tmp); Py_DECREF(ans1); return NULL; } Py_DECREF(tmp); } else { ans2 = tmp; } } else if (divmod == 2) { ans2 = _decimal_get_copy(self); if (!ans2) { Py_DECREF(ans1); return NULL; } } ans = Py_BuildValue("(OO)", ans1, ans2); Py_DECREF(ans1); Py_DECREF(ans2); return ans; } } op1 = self; op2 = other; /* if divmod, then op1->exp - op2->exp must be divisible by LOG */ if (divmod) { exp_t expdiff = exp_sub(op1->exp, op2->exp); exp_t tmp_exp = op1->exp; /* we will do in loop, because of some issues with x % a, when x < 0 */ while (exp_mod_i(expdiff, LOG)) { exp_dec(&tmp_exp); expdiff = exp_sub(tmp_exp, op2->exp); } op1 = _decimal_rescale(self, tmp_exp, ctx, -1, 0); } if (1) { long prec_needed; /* how many significant digits we need */ long significant_limbs; /* how many significant limbs we need to achieve prec */ decimalobject *result; decimalobject *remainder_ret; long *remainder; exp_t exp = exp_sub(op1->exp, op2->exp); exp_t expdiff; long rlimbs; long old_size; exp_t adjusted = exp_from_i(0); long i; long max_size; exp_t min_expdiff; /* used when divmod */ long remainder_limbs = op2->limb_count+1 > op1->limb_count ? op2->limb_count + 1 : op1->limb_count; /* if !shouldround, then we need log2(divider.int) + log10(op1.int) * + some constant, to make sure, division is possible */ if (!shouldround && !divmod) prec_needed = op2->ob_size * 4 + op1->ob_size + 5; /* we need only ctx->prec + 1 and remainder */ else prec_needed = ctx->prec+1; /* we need (prec_needed + LOG -1)/ LOG rounded up limbs, because * it may happen, that first limb is between 1 and 9, so we'll * get (significant_limbs-1) * LOG + 1 digits */ significant_limbs = (prec_needed + LOG -1) / LOG; significant_limbs += ((prec_needed + LOG -1) % LOG) != 0; remainder = (long*) PyMem_MALLOC((remainder_limbs) * sizeof(long)); memset(remainder, 0, sizeof(long) * (remainder_limbs)); if (!remainder) { PyErr_NoMemory(); return NULL; } /* we create room for additional limb, in case remainder != 0 * then, we will just move results->limbs one left, and set first * limb 1, to make rounding working properly */ result = _NEW_decimalobj(significant_limbs * LOG + LOG, sign, exp_from_i(0)); for (i = 0; i < result->limb_count; i++) result->limbs[i] = 0; result->ob_size -= LOG; result->limb_count --; if (!result) { PyMem_FREE(remainder); return NULL; } /* we want exp + expdiff * LOG + LOG >= 0 so: * expdiff * LOG >= -exp - LOG * expdiff >= (-exp - LOG)/ LOG */ if (divmod) { assert(!(exp_mod_i(exp, LOG))); min_expdiff = exp_sub_i(exp_neg(exp), LOG); min_expdiff = exp_floordiv_i(min_expdiff, LOG); } else min_expdiff = exp_from_i(op1->limb_count); /* EXP TODO ?? (min_expdiff) */ expdiff = exp_from_i(_limb_divide(op1->limbs, op1->limb_count, op2->limbs, op2->limb_count, result->limbs, significant_limbs, remainder, exp_to_i(min_expdiff))); result->limbs[significant_limbs] = 0; exp_inp_add(&exp, exp_add_i(exp_mul_i(expdiff, LOG), LOG)); rlimbs = _limb_size(remainder, remainder_limbs); /* remainder non-zero */ if (!(rlimbs == 1 && remainder[0] == 0)) { /* we have not enough precision to do exact integer division */ if (divmod && exp_is_neg(exp)) { Py_DECREF(op1); Py_DECREF(result); PyMem_FREE(remainder); return handle_DivisionImpossible(self->ob_type, ctx, NULL); } if (!divmod) { exp_inp_sub_i(&exp, LOG); result->limb_count ++; result->ob_size += LOG; _limb_move_left(result->limbs, result->limb_count, 1); result->limbs[0] = 1; } } if (divmod) { remainder_ret = _NEW_decimalobj(rlimbs * LOG, self->sign, exp_from_i(0)); if (!remainder_ret) { Py_DECREF(op1); Py_DECREF(result); PyMem_FREE(remainder); return NULL; } for (i = 0; i< rlimbs; i++) remainder_ret->limbs[i] = remainder[i]; if (exp_le_i(expdiff, 0)) remainder_ret->exp = exp_add(op1->exp, exp_mul_i( expdiff, LOG)); else remainder_ret->exp = op1->exp; remainder_ret->ob_size = _limb_size_s(remainder_ret->limbs, remainder_ret->ob_size); remainder_ret->limb_count = (remainder_ret->ob_size + LOG -1)/LOG; } old_size = result->ob_size; result->ob_size = _limb_size_s(result->limbs, result->ob_size); result->limb_count = (result->ob_size + LOG -1)/ LOG; result->exp = exp; max_size = op1->ob_size > op2->ob_size ? op1->ob_size : op2->ob_size; /* adjusted is computed just like in the python code */ adjusted = exp_from_i(self->ob_size - other->ob_size + 1); for (i = 0;i < max_size ;i++) { long x1,x2; x1 = _limb_get_digit(self->limbs, self->ob_size, i); x2 = _limb_get_digit(other->limbs, other->ob_size, i); if (x2 > x1) { exp_dec(&adjusted); break; } } assert(op1->ob_size == _limb_size_s(op1->limbs, op1->ob_size)); assert(op2->ob_size == _limb_size_s(op2->limbs, op2->ob_size)); /* to be compatible with python implementation, result int must * have more than adjusted digits =] */ while (exp_l_i(adjusted, result->ob_size) && _limb_get_digit(result->limbs, result->ob_size, result->ob_size -1) == 0) { /* when int dividing, exp should be 0 */ if (exp_ge_i(result->exp, 0) && divmod) break; _limb_cut_one_digit(result->limbs, result->ob_size); result->ob_size --; exp_inc(&(result->exp)); } result->limb_count = (result->ob_size + LOG -1)/LOG; if (exp_g_i(exp_add_i(result->exp, result->ob_size), ctx->prec) && shouldround && divmod) { Py_DECREF(remainder_ret); Py_DECREF(result); Py_DECREF(op1); PyMem_FREE(remainder); return handle_DivisionImpossible(self->ob_type, ctx, NULL); } /* TODO this *need* clean up */ if (divmod) { /* we need to rescale, to be compatible with python implementation */ PyObject *flags = 0; contextobject *ctx2 = 0; exp_t remainder_exp = exp_min(self->exp, other->exp); decimalobject *rescaled_rem = 0; decimalobject *rescaled = _decimal_rescale(result, exp_from_i(0), ctx, -1, 0); if (!rescaled) { Py_DECREF(result); Py_DECREF(remainder_ret); Py_DECREF(op1); return NULL; } ctx2 = context_copy(ctx); if (!ctx2) { Py_DECREF(rescaled); Py_DECREF(result); Py_DECREF(remainder_ret); Py_DECREF(op1); return NULL; } flags = context_ignore_all_flags(ctx2); if (!flags) { Py_DECREF(ctx2); Py_DECREF(rescaled); Py_DECREF(result); Py_DECREF(remainder_ret); Py_DECREF(op1); return NULL; } rescaled_rem = _decimal_rescale(remainder_ret, remainder_exp, ctx2, -1, 0); Py_DECREF(ctx2); Py_DECREF(flags); Py_DECREF(remainder_ret); if (!rescaled_rem) { Py_DECREF(rescaled); Py_DECREF(result); Py_DECREF(op1); return NULL; } if (shouldround) { decimalobject *fixed = _decimal_fix(rescaled_rem, ctx); if (!fixed) { Py_DECREF(rescaled); Py_DECREF(result); Py_DECREF(op1); Py_DECREF(rescaled_rem); return NULL; } Py_DECREF(rescaled_rem); rescaled_rem = fixed; } ans = Py_BuildValue("(OO)", rescaled, rescaled_rem); Py_DECREF(rescaled); Py_DECREF(result); Py_DECREF(rescaled_rem); Py_DECREF(op1); } if (shouldround && !divmod) { decimalobject *fixed; fixed = _decimal_fix(result, ctx); Py_DECREF(result); ans = (PyObject*) fixed; } else if (!divmod){ ans = (PyObject*) result; } PyMem_FREE(remainder); return ans; } Py_RETURN_NONE; } static PyObject * decimal__divide(decimalobject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"other", "divmod", "context", 0}; contextobject *ctx = NULL; int divmod = 0; PyObject *other, *ret; /* what should we DECREF */ decimalobject *dec = 0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iO:_divide", kwlist, &other, &divmod, &ctx)) return NULL; if (!ctx) if (!(ctx = getcontext())) return NULL; if (PyDecimal_Check(self)) { other = _convert_to_decimal(self->ob_type, other, ctx, 0); if (!other || other == Py_NotImplemented) return other; dec = (decimalobject*)other; } else { assert(PyDecimal_Check(other)); self = (decimalobject*)_convert_to_decimal(other->ob_type, (PyObject*)self, ctx, 0); if (!self || (PyObject*)self == Py_NotImplemented) return (PyObject*)self; dec = (decimalobject*)self; } if (!PyDecimalContext_Check(ctx)) { PyErr_SetString(PyExc_TypeError, "context must be Context object"); Py_DECREF(dec); return NULL; } ret = _do_decimal__divide(self, (decimalobject*)other, divmod, ctx); Py_DECREF(dec); return ret; } static int check_ctx(decimalobject *self, contextobject *ctx) { if (ctx->prec > MAX_MATH || exp_g_i(ctx->Emax, MAX_MATH) || exp_l_i(ctx->Emin,-1 * MAX_MATH)) return 0; return 1; } /* The idea is as follows, we need to calculate e^x (self = x), * we use series e^x = x^0/0! + x^1/1! + x^2/2! ... * But first, we divide x, by integer, so that x < 1 - this results * in much faster calculation. So we have e^(x/a), where a = 10^h, * when we got e^(x/a) we raise it to power a. H is at most 8. */ static decimalobject * _do_decimal_exponent(decimalobject *self, contextobject *ctx) { decimalobject *ans; long h; int clamped; long precision, prec; int rounding; exp_t new_exp; if (!ctx) ctx = getcontext(); if (!ctx) return NULL; if (ISSPECIAL(self)) { decimalobject *nan; if (_check_nans(self, NULL, ctx, &nan)) return nan; /* -Infinity -> +0 */ /* Infinity -> self */ if (ISINF(self)) { if (self->sign == SIGN_NEGINF) { ans = _NEW_decimalobj(1, 0, exp_from_i(0)); if (!ans) return NULL; ans->limbs[0] = 0; return ans; } else { ans = _decimal_get_copy(self); return ans; } } } if (!decimal_nonzero(self)) { ans = _NEW_decimalobj(1, 0, exp_from_i(0)); if (!ans) return NULL; ans->limbs[0] = 1; return ans; } h = exp_to_i(ADJUSTED(self)) + 1; precision = ctx->prec; clamped = ctx->clamp; rounding = ctx->rounding; ctx->rounding = ROUND_HALF_EVEN; /* this will be changed */ if (h > 8) { h = 8; prec = 9; ans = _NEW_decimalobj(1, 0, exp_from_i(0)); if (!ans) return NULL; ans->limbs[0] = 2; if (self->sign&1) { ans->exp = exp_from_i(-2); } } else { decimalobject *t; /* that's what we add */ decimalobject *d; /* divisor */ decimalobject *rescaled; contextobject *ctx2; d = _NEW_decimalobj(1, 0, exp_from_i(0)); if (!d) return NULL; d->limbs[0] = 2; if (h < 0) h = 0; /* we increase h, so self is smaller and series go faster */ if (self->ob_size > 8 && h < 8) h ++; new_exp = exp_sub_i(self->exp, h); if (h) { rescaled = _decimal_get_copy(self); if (!rescaled) { Py_DECREF(d); return NULL; } rescaled->exp = new_exp; } else { Py_INCREF(self); rescaled = self; } prec = (ctx->prec > self->ob_size ? ctx->prec : self->ob_size) + h + 2; ctx2 = context_copy(ctx); if (!ctx2) { Py_DECREF(rescaled); Py_DECREF(d); return NULL; } t = _decimal_get_copy(rescaled); if (!t) { Py_DECREF(rescaled); Py_DECREF(ctx2); Py_DECREF(d); return NULL; } ctx2->Emin = exp_from_i(-999999999); ctx->clamp = 0; ctx->prec = 2*prec; ctx2->prec = prec; ctx2->rounding = ROUND_HALF_EVEN; ans = _NEW_decimalobj(1, 0, exp_from_i(0)); if (!ans) { Py_DECREF(rescaled); Py_DECREF(ctx2); Py_DECREF(d); Py_DECREF(t); ctx->prec = precision; ctx->clamp = clamped; return NULL; } ans->limbs[0] = 1; while (1) { decimalobject *tmp; tmp = _do_decimal_add(ans, t, ctx); Py_DECREF(ans); if (!tmp) { Py_DECREF(rescaled); Py_DECREF(ctx2); Py_DECREF(d); Py_DECREF(t); ctx->prec = precision; ctx->clamp = clamped; return NULL; } ans = tmp; tmp = _do_decimal_multiply(t, rescaled, ctx2); Py_DECREF(t); if (!tmp) { Py_DECREF(rescaled); Py_DECREF(ctx2); Py_DECREF(d); Py_DECREF(ans); ctx->prec = precision; ctx->clamp = clamped; return NULL; } t = tmp; tmp = (decimalobject*)_do_decimal__divide(t, d, 0, ctx2); Py_DECREF(t); if (!tmp) { Py_DECREF(rescaled); Py_DECREF(ctx2); Py_DECREF(d); Py_DECREF(ans); ctx->prec = precision; ctx->clamp = clamped; return NULL; } /* we end, when |x^i/i!| cannot affect result * we now, that when S(i) = x^0/0! + x^1/1! ... x^i/i!, then * S(i) - x^i/i! <= e^x <= S(i) + x^i/i! */ t = tmp; if (ans->ob_size >= prec) { exp_t tmp_exp = exp_add_i(ADJUSTED(t), prec + 1); if (exp_ge(ADJUSTED(ans), tmp_exp)) break; } ctx2->prec = 16; ctx2->Emax = exp_from_i(1000); tmp = _decimal_increment(d, 1, ctx2); Py_DECREF(d); ctx2->prec = prec; ctx2->Emax = ctx->Emax; if (!tmp) { Py_DECREF(rescaled); Py_DECREF(ctx2); Py_DECREF(d); Py_DECREF(ans); ctx->prec = precision; ctx->clamp = clamped; } d = tmp; } Py_DECREF(t); Py_DECREF(d); Py_DECREF(rescaled); Py_DECREF(ctx2); } ctx->prec = prec + 2; if (h) { long pow; decimalobject *y; decimalobject *tmp; int i; pow = 1; for (i = 0; i < h; i++) pow *= 10; y = (decimalobject*)decimal_from_long(self->ob_type, pow); if (!y) { Py_DECREF(ans); ctx->prec = precision; ctx->clamp = clamped; return NULL; } Py_INCREF(Py_None); tmp = _do_decimal_power(ans, y, (decimalobject*)Py_None, ctx); Py_DECREF(Py_None); Py_DECREF(y); Py_DECREF(ans); if (!tmp) { ctx->prec = precision; ctx->clamp = clamped; return NULL; } ans = tmp; } ctx->clamp = clamped; ctx->prec = precision; /* we'll add 1 at the end, to make proper rounding */ if (!ISSPECIAL(ans) && decimal_nonzero(ans)) { long i; decimalobject *tmp = _NEW_decimalobj(ans->ob_size + LOG, ans->sign, exp_sub_i(ans->exp, LOG)); if (!tmp) { Py_DECREF(ans); return NULL; } for (i = 0; i limb_count;i++) { tmp->limbs[i+1] = ans->limbs[i]; } tmp->limbs[0] = 1; Py_DECREF(ans); ans = tmp; } ctx->rounding = rounding; { decimalobject *fixed = _decimal_fix(ans, ctx); Py_DECREF(ans); ans = fixed; } return ans; } DECIMAL_UNARY_FUNC(exponent) static PyObject * _do_decimal_exp(decimalobject *self, contextobject *ctx) { if (!ctx) ctx = getcontext(); if (!ctx) return NULL; if (!check_ctx(self, ctx)) return (PyObject*)handle_InvalidContext(self->ob_type, ctx, NULL); return (PyObject*)_do_decimal_exponent(self, ctx); } DECIMAL_UNARY_FUNC(exp) int ln_lookup[] = {9016, 8652, 8316, 8008, 7724, 7456, 7208, 6972, 6748, 6540, 6340, 6148, 5968, 5792, 5628, 5464, 5312, 5164, 5020, 4884, 4748, 4620, 4496, 4376, 4256, 4144, 4032, 39233, 38181, 37157, 36157, 35181, 34229, 33297, 32389, 31501, 30629, 29777, 28945, 28129, 27329, 26545, 25777, 25021, 24281, 23553, 22837, 22137, 21445, 20769, 20101, 19445, 18801, 18165, 17541, 16925, 16321, 15721, 15133, 14553, 13985, 13421, 12865, 12317, 11777, 11241, 10717, 10197, 9685, 9177, 8677, 8185, 7697, 7213, 6737, 6269, 5801, 5341, 4889, 4437, 39930, 35534, 31186, 26886, 22630, 18418, 14254, 10130, 6046, 20055}; /* this function passes all tests, but I am not sure if it's always gives answer with * error < 0.5 ulp. I am not sure if there exists such solution (it's ok if we just truncate * digits, but problem arises when using rounding which checks if last digit is 5 * and result ens with 50000...00001) */ static decimalobject * _do_decimal__ln(decimalobject *self, contextobject *ctx) { decimalobject *ans = 0, *b = 0, *tmp, *one = 0; contextobject *ctx1 = 0, *ctx2 = 0; PyObject *flags; int rounding; long prec, cp; int t; if (!ctx) ctx = getcontext(); if (!ctx) return NULL; if (ISSPECIAL(self)) { decimalobject *nan; if (_check_nans(self, NULL, ctx, &nan) != 0) return nan; /*-inf -> error * inf -> self */ if (ISINF(self)) { if (self->sign &1) return handle_InvalidOperation(self->ob_type, ctx, "ln(-inf)", NULL); else return _decimal_get_copy(self); } } if (!decimal_nonzero(self)) { ans = _NEW_decimalobj(1, SIGN_NEGINF, exp_from_i(0)); return ans; } if (self->sign &1) { return handle_InvalidOperation(self->ob_type, ctx, "ln(-x)", NULL); } one = _NEW_decimalobj(1, 0, exp_from_i(0)); if (!one) return NULL; one->limbs[0] = 1; ctx1 = context_copy(ctx); if (!ctx1) goto err; flags = context_ignore_all_flags(ctx1); if (!flags) goto err; Py_DECREF(flags); ctx1->prec = 16; prec = (self->ob_size > ctx->prec ? self->ob_size : ctx->prec) + 2; prec = prec > 9 ? prec : 9; ans = (decimalobject*)decimal_from_long(self->ob_type, exp_to_i(ADJUSTED(self)) + 1); if (!ans) goto err; b = (decimalobject*)decimal_from_long(self->ob_type, 2302585); if (!b) goto err; b->exp = exp_from_i(-6); tmp = _do_decimal_multiply(ans, b, ctx1); if (!tmp) goto err; Py_DECREF(ans); ans = tmp; t = _limb_get_digit(self->limbs, self->ob_size, 0); if (self->ob_size > 1) { t *= 10; t += _limb_get_digit(self->limbs, self->ob_size, 1); } if (t<10) t *= 10; t = ln_lookup[t-10]; Py_DECREF(b); b = (decimalobject*)decimal_from_long(self->ob_type, t >> 2); if (!b) goto err; b->exp = exp_from_i(-(t&3) - 3); b->sign = 1; rounding = ctx1->rounding; /* we'll calculate lower bound, There might be problem with * result which ends wit 50...01 (I dont really think there is rational a, * that ln(a) ends with 500000.... - and we actually operate on rational subset * of R) */ ctx1->rounding = ROUND_DOWN; tmp = _do_decimal_add(ans, b, ctx1); if (!tmp) goto err; Py_DECREF(ans); ans = tmp; ctx1->prec = ctx->prec; ctx1->clamp = 0; ctx2 = context_shallow_copy(ctx1); if (!ctx2) goto err; ctx2->Emax = exp_from_i(2 * MAX_MATH); ctx2->Emin = exp_from_i(-2 * MAX_MATH); cp = 9; /* precision we'll get after every iteration */ ctx1->prec = cp; ctx2->prec = cp + self->ob_size; while (1) { ans->sign ^= 1; Py_DECREF(b); b = _do_decimal_exponent(ans, ctx2); if (!b) goto err; ans->sign ^= 1; tmp = _do_decimal_multiply(b, self, ctx2); if (!tmp) goto err; Py_DECREF(b); b = tmp; tmp = _do_decimal_subtract(b, one, ctx2); if (!tmp) goto err; Py_DECREF(b); b = tmp; /* ADJUSTED(ans) >= ADJUSTED(b) + ctx->prec + 1 */ if (!decimal_nonzero(b) || exp_ge(ADJUSTED(ans), exp_add_i(ADJUSTED(b), ctx->prec + 1))) { if (ans->ob_size == prec) break; if (!decimal_nonzero(ans)) { if (!_do_real_decimal_compare(self, one, ctx1)) { ans->exp = exp_from_i(0); break; } else { if (handle_Rounded(ctx, NULL)) goto err; if (handle_Inexact(ctx, NULL)) goto err; } } /* to make addition easier */ if (!decimal_nonzero(b)) b->exp = exp_sub_i(ans->exp, prec); } tmp = _do_decimal_add(ans, b, ctx1); if (!tmp) goto err; Py_DECREF(ans); ans = tmp; /* we're done */ if (cp == prec) continue; cp *= 2; if (cp > prec) cp = prec; ctx1->prec = cp; ctx2->prec = cp + self->ob_size; } rounding = ctx->rounding; ctx->rounding = ROUND_HALF_EVEN; /* we add extra 1 at the end to make proper rounding only if last digit is not 0 (to avoid 0.9999999...) */ if (decimal_nonzero(ans) && !ISSPECIAL(ans) && (ans->limbs[0] % 10) != 9) { int i; tmp = _NEW_decimalobj(ans->ob_size + LOG, ans->sign, exp_sub_i(ans->exp, LOG)); if (!tmp) { ctx->rounding = rounding; goto err; } for (i = 0; i < ans->limb_count ; i++){ tmp->limbs[i+1] = ans->limbs[i]; } tmp->limbs[0] = 1; Py_DECREF(ans); ans = tmp; } ctx->rounding = rounding; tmp = _decimal_fix(ans, ctx); if (!tmp) goto err; Py_DECREF(ans); ans = tmp; Py_DECREF(b); Py_DECREF(ctx1); Py_DECREF(ctx2); Py_DECREF(one); return ans; err: Py_DECREF(one); Py_XDECREF(ans); Py_XDECREF(b); Py_XDECREF(ctx1); Py_XDECREF(ctx2); return NULL; } DECIMAL_UNARY_FUNC(_ln) static PyObject * _do_decimal_ln(decimalobject *self, contextobject *ctx) { if (!check_ctx(self, ctx)) return (PyObject*)handle_InvalidContext(self->ob_type, ctx, NULL); return (PyObject*)_do_decimal__ln(self, ctx); } DECIMAL_UNARY_FUNC(ln) static decimalobject * _do_decimal_log10(decimalobject *self, contextobject *ctx) { decimalobject *ret; long prec; contextobject *ctx2; PyObject *flags; if (!ctx) ctx = getcontext(); if (!ctx) return NULL; if (!check_ctx(self, ctx)) return handle_InvalidContext(self->ob_type, ctx, NULL); if (ISSPECIAL(self)) { decimalobject *nan; if (_check_nans(self, NULL, ctx, &nan) != 0) return nan; if (ISINF(self)) { if (self->sign &1) return handle_InvalidOperation(self->ob_type, ctx, "log10(-inf)", NULL); else return _decimal_get_copy(self); } } if (self->sign &1 && decimal_nonzero(self)) return handle_InvalidOperation(self->ob_type, ctx, "log10(-x)", NULL); /* first, check if self is power of 10*/ if (_limb_get_digit(self->limbs, self->ob_size, 0) == 1) { /* SLOW */ int i; for (i=1;i ob_size;i++) { if (_limb_get_digit(self->limbs, self->ob_size, i)) break; } /* self is power of 10 */ if (i == self->ob_size) { decimalobject *fixed; /* TODO what about big exp? */ ret = (decimalobject*)decimal_from_long(self->ob_type, exp_to_i(ADJUSTED(self))); if (!ret) return NULL; fixed = _decimal_fix(ret, ctx); Py_DECREF(ret); return fixed; } } /* we use context precision, or self->ob_size + 6 (biggest size of exponent) * + 3 as precision */ prec = self->ob_size + 6 > ctx->prec ? self->ob_size+9: ctx->prec+3; ctx2 = context_shallow_copy(ctx); if (!ctx2) return NULL; ctx2->prec = prec; ctx2->Emax = exp_from_i(MAX_MATH); ctx2->Emin = exp_from_i(-MAX_MATH); { decimalobject *a; /* ln(self) */ decimalobject *ten; /* Decimal(10) */ decimalobject *ln10; /* ln(10) */ a = _do_decimal__ln(self, ctx2); if (!a) { Py_DECREF(ctx2); return NULL; } ten = _NEW_decimalobj(2, 0, exp_from_i(0)); if (!ten) { Py_DECREF(a); Py_DECREF(ctx2); return NULL; } ten->limbs[0] = 10; flags = context_ignore_all_flags(ctx2); if (!flags) { Py_DECREF(a); Py_DECREF(ctx2); Py_DECREF(ten); return NULL; } ctx2->prec = ctx->prec + 3; ln10 = _do_decimal__ln(ten, ctx2); Py_DECREF(ten); if (!ln10) { Py_DECREF(a); Py_DECREF(ctx2); Py_DECREF(flags); return NULL; } { PyObject *tmp = context_regard_flags(ctx2, flags); Py_DECREF(flags); if (!tmp) { Py_DECREF(a); Py_DECREF(ctx2); return NULL; } Py_DECREF(tmp); } Py_DECREF(ctx2); /* log10 = ln(self)/ln(10) */ ret = (decimalobject*)_do_decimal__divide(a, ln10, 0, ctx); Py_DECREF(a); Py_DECREF(ln10); return ret; } } DECIMAL_UNARY_FUNC(log10) /* due to some nasty special cases, this code looks quite ugly */ static int _do_real_decimal_comparetotal(decimalobject *self, decimalobject *other, contextobject *ctx) { exp_t adj_self; exp_t adj_other; /* what should we return when less, greater, equal ie: * when both are > 0 and aren't special then l = -1 g = 1 eq = 0 * when both are < 0 and aren't special then l = 1 g = -1 eq = 0 * when self is sNaN and other is NaN then l and g are as above, but eq = -1 * etc ... */ int when_l; int when_g; int cmp; /* comparison of abs(self) and abs(other) (remember, abs(NaN50) = 50 ) */ if (!ctx) ctx = getcontext(); if (!ctx) return 0; /* sign is most important */ if ((other->sign &1) && !(self->sign&1)) return 1; if ((self->sign &1) && !(other->sign&1)) return -1; if (self->sign &1) { when_l = 1; when_g = -1; } else { when_l = -1; when_g = 1; } /* abs(nan) always greater than abs(non-nan) */ if (GETNAN(self) && !GETNAN(other)) return when_g; if (GETNAN(other) && !GETNAN(self)) return when_l; /* both are nans */ if (GETNAN(self)) { /* one is sNaN, and one NaN, in this case, when abs(sNaN) == abs(NaN) * then NaN is greater if both signs are + and is less, if both signs * are - */ if (GETNAN(self) != GETNAN(other)) { if (GETNAN(self) == 2) return when_l; else return when_g; /* now 'add' sign to this */ } } if (ISINF(self) || ISINF(other)) { if (ISINF(self) && ISINF(other)) return 0; if (ISINF(self) && !ISINF(other)) return when_g; return when_l; } /* we now know that self->sign == other->sign, and everything is just fine ;P */ adj_self = ADJUSTED(self); adj_other = ADJUSTED(other); if (exp_eq(adj_self, adj_other)) { long digits = self->ob_size > other->ob_size ? self->ob_size : other->ob_size; long i; /* SLOW */ for (i=0;i limbs, self->ob_size, i); d_o = _limb_get_digit(other->limbs, other->ob_size, i); if (d_s != d_o) { if (d_s > d_o) cmp = 1; else cmp = -1; break; } } /* equal */ /* abs(0.0000) > abs(0.0) */ if (i == digits) { if (exp_eq(self->exp, other->exp)) { cmp = 0; } else if (exp_g(self->exp, other->exp)) cmp = 1; else cmp = -1; } } else if (exp_g(adj_self, adj_other)) cmp = 1; else cmp = -1; switch (cmp) { case 1: return when_g; break; case 0: return 0; break; case -1: return when_l; break; } /* we should not reach here */ assert(0); return -1; } static decimalobject * _do_decimal_comparetotal(decimalobject *self, decimalobject *other, contextobject *ctx) { int res; res = _do_real_decimal_comparetotal(self, other, ctx); return (decimalobject*)decimal_from_long(self->ob_type, res); } DECIMAL_BINARY_FUNC(comparetotal) static PyMethodDef decimal_methods[] = { {"comparetotal", (PyCFunction)decimal_comparetotal, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("TODO")}, {"log10", (PyCFunction)decimal_log10, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("TODO")}, {"ln", (PyCFunction)decimal_ln, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("TODO")}, {"_ln", (PyCFunction)decimal__ln, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("TODO")}, {"exp", (PyCFunction)decimal_exp, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("TODO")}, {"_exponent", (PyCFunction)decimal_exponent, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("TODO")}, {"_divide", (PyCFunction)decimal__divide, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("TODO")}, {"_rescale", (PyCFunction)decimal_rescale, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("asdf")}, /* TODO */ {"_fix", (PyCFunction)decimal_fix, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("asdf")}, /* TODO */ {"adjusted", (PyCFunction)decimal_adjusted, METH_NOARGS, PyDoc_STR("Return the adjusted exponent of self.")}, {"as_tuple", (PyCFunction)decimal_as_tuple, METH_NOARGS, PyDoc_STR("Represents the number as a triple tuple.\n\n" "To show the internals exactly as they are.")}, {"compare", (PyCFunction)decimal_compare, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("Compares one to another.\n\n -1 => a < b\n" " 0 => a = b\n 1 => a > b\n" " NaN => one is NaN\n" "Like __cmp__, but returns Decimal instances.")}, {"max", (PyCFunction)decimal_max, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("Returns the larger value.\n\n" "like max(self, other) except if one is not a number, \n" "returns NaN (and signals if one is sNaN). Also rounds.")}, {"min", (PyCFunction)decimal_min, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("Returns the smaller value.\n\n" "like min(self, other) except if one is not a number, \n" "returns NaN (and signals if one is sNaN). Also rounds.")}, {"normalize", (PyCFunction)decimal_normalize, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("Normalize- strip trailing 0s, change anything equal" "to 0 to 0e0")}, {"trim", (PyCFunction)decimal_trim, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("TODO")}, {"quantize", (PyCFunction)decimal_quantize, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("Quantize self so its exponent is the same as that of exp.\n\n" "Similar to self._rescale(exp._exp) but with error checking.")}, {"remainder_near", (PyCFunction)decimal_remainder_near, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("Remainder nearest to 0- abs(remainder-near) <= other/2")}, {"same_quantum", (PyCFunction)decimal_same_quantum, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("Test whether self and other have the same exponent.\n\n" "same as self._exp == other._exp, except NaN == sNaN")}, {"sqrt", (PyCFunction)decimal_sqrt, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("Return the square root of self.\n\n" "Uses a converging algorithm (Xn+1 = 0.5*(Xn + self / Xn))\n" "Should quadratically approach the right answer.")}, {"to_eng_string", (PyCFunction)decimal_to_eng_string, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("Convert to engineering-type string.\n\n" "Engineering notation has an exponent which is a multiple " "of 3, so there\nare up to 3 digits left of the decimal " "place.\n\nSame rules for when in exponential and when as " "a value as in __str__.")}, {"to_integral", (PyCFunction)decimal_to_integral, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("Rounds to the nearest integer, without raising " "inexact, rounded.")}, {"__reduce__", (PyCFunction)decimal_reduce, METH_NOARGS}, {"__copy__", (PyCFunction)decimal_copy_method, METH_NOARGS}, {"__deepcopy__", decimal_deepcopy, METH_O}, /* XXX: only helper methods below */ {"_round", (PyCFunction)decimal_XXX_round, METH_VARARGS}, {"abs__", (PyCFunction)decimal_XXX_abs, METH_VARARGS}, {NULL, NULL} }; static char decimal_doc[] = PyDoc_STR("Floating point class for decimal arithmetic."); /* Declare a "special" (double-underscore) method with two arguments for use in PyNumberMethods slots. As this can take any objects, but other code in this module wants to call some of those methods too, this wrapper is a convenience */ /* Actually, self might be possibly not decimal I assume that self or other * will be =] */ #define DECIMAL_SPECIAL_2FUNC(func) \ static PyObject * \ func (PyObject *self, PyObject *other) { \ decimalobject *res; \ PyObject *to_decref; \ contextobject *ctx = getcontext(); \ if (!ctx) return NULL; \ if(PyDecimal_Check(self)) \ { \ other = (PyObject*)_convert_to_decimal(self->ob_type, other, ctx, 0); \ if (other == NULL || other == Py_NotImplemented) return other; \ to_decref = other; \ } \ else \ { \ assert(PyDecimal_Check(other)); \ self = (PyObject*)_convert_to_decimal(other->ob_type, self, ctx, 0); \ to_decref = self; \ if(self == NULL || self == Py_NotImplemented) return self; \ } \ res = _do_##func((decimalobject *)self, \ (decimalobject *)other, ctx); \ Py_DECREF(to_decref); \ return (PyObject *)res; \ } /* Same for one-argument methods. */ #define DECIMAL_SPECIAL_1FUNC(func) \ static PyObject * \ func (PyObject *self) { \ contextobject *ctx = getcontext(); \ if (!ctx) return NULL; \ return (PyObject *)_do_##func((decimalobject *)self, ctx); \ } static decimalobject * _do_decimal_add(decimalobject *self, decimalobject *other, contextobject *ctx) { int shouldround, sign, negativezero = 0; exp_t exp, oexp; long prec,cmp; decimalobject *res, *res2, *ret, *o1, *o2; decimalobject *tmp, *oother; long numdigits; assert(PyDecimal_Check(other)); assert(PyDecimal_Check(self)); prec = ctx->prec; if (ISSPECIAL(self) || ISSPECIAL(other)) { decimalobject *nan; if (_check_nans(self, other, ctx, &nan) != 0) return nan; if (ISINF(self)) { if (((self->sign&1) != (other->sign&1)) && ISINF(other)) return handle_InvalidOperation(self->ob_type, ctx, "-INF + INF", NULL); return _decimal_get_copy(self); } else if (ISINF(other)) { return _decimal_get_copy(other); } } shouldround = (ctx->rounding_dec == ALWAYS_ROUND); exp = exp_min(self->exp, other->exp); if (ctx->rounding == ROUND_FLOOR && (self->sign & 1) != (other->sign & 1)) negativezero = 1; if (!decimal_nonzero(self) && !decimal_nonzero(other)) { if (negativezero) sign = 1; else if (self->sign < other->sign) sign = self->sign; else sign = other->sign; res = _new_decimalobj(self->ob_type, 1, sign, exp); if (!res) return NULL; res->limbs[0] = 0; ret = _decimal_fix(res, ctx); Py_DECREF(res); if (!ret) return NULL; return ret; } if (!decimal_nonzero(self)) { oexp = exp_sub_i(other->exp, ctx->prec + 1); exp = exp_max(exp, oexp); res = _decimal_rescale(other, exp, ctx, 0, 0); if (!res) return NULL; if (shouldround) { res2 = _decimal_fix(res, ctx); Py_DECREF(res); return res2; } else { return res; } } if (!decimal_nonzero(other)) { oexp = exp_sub_i(self->exp, ctx->prec + 1); exp = exp_max(exp, oexp); res = _decimal_rescale(self, exp, ctx, 0, 0); if (!res) return NULL; if (shouldround) { res2 = _decimal_fix(res, ctx); Py_DECREF(res); return res2; } else { return res; } } numdigits = exp_to_i(exp_sub(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, exp_sub_i(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, exp_sub_i(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(exp_eq(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(exp_l(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; Py_XDECREF(o1); Py_XDECREF(o2); return NULL; } DECIMAL_SPECIAL_2FUNC(decimal_add) static decimalobject * _do_decimal_subtract(decimalobject *self, decimalobject *other, contextobject *ctx) { decimalobject *copy, *res; if (ISSPECIAL(self) || ISSPECIAL(other)) { decimalobject *nan; if (_check_nans(self, other, ctx, &nan) != 0) return nan; } copy = _decimal_get_copy(other); if (!copy) return NULL; /* flip sign */ if (copy->sign & 1) copy->sign--; else copy->sign++; res = _do_decimal_add(self, copy, ctx); Py_DECREF(copy); return res; } DECIMAL_SPECIAL_2FUNC(decimal_subtract) /* it's just so straightforward, wish adding was the same ;P */ static decimalobject * _do_decimal_multiply(decimalobject *self, decimalobject *other, contextobject *ctx) { long resultsign; exp_t resultexp; long shouldround; long i; long max_limbs; long size; decimalobject *ans; resultsign = (self->sign&1) ^ (other->sign&1); if(ISSPECIAL(self) || ISSPECIAL(other)) { decimalobject *nan; if(_check_nans(self, other, ctx, &nan)) return nan; if(ISINF(self)) { if(decimal_nonzero(other)) { ans = _NEW_decimalobj(1, resultsign ? SIGN_NEGINF : SIGN_POSINF, exp_from_i(0)); return ans; } return handle_InvalidOperation(self->ob_type, ctx, "(+-)INF * 0", NULL); } if(ISINF(other)) { if(decimal_nonzero(self)) { ans = _NEW_decimalobj(1, resultsign ? SIGN_NEGINF : SIGN_POSINF, exp_from_i(0)); return ans; } return handle_InvalidOperation(self->ob_type, ctx, "0 * (+-)INF", NULL); } } resultexp = exp_add(self->exp, other->exp); shouldround = ctx->rounding_dec == ALWAYS_ROUND; if(!decimal_nonzero(self) || !decimal_nonzero(other)) { ans = _NEW_decimalobj(1, resultsign, resultexp); if(!ans) return NULL; ans->limbs[0] = 0; goto done; } if(self->ob_size == 1 && self->limbs[0] == 1) { ans = _NEW_decimalobj(other->ob_size, resultsign, resultexp); if(!ans) return NULL; for(i = 0; i limb_count;i++) ans->limbs[i] = other->limbs[i]; goto done; } if(other->ob_size == 1 && other->limbs[0] == 1) { ans = _NEW_decimalobj(self->ob_size, resultsign, resultexp); if(!ans) return NULL; for(i = 0; i limb_count; i++) ans->limbs[i] = self->limbs[i]; goto done; } max_limbs = (self->ob_size + LOG -1)/ LOG + (other->ob_size + LOG -1)/LOG; ans = _NEW_decimalobj(max_limbs * LOG, resultsign, resultexp); if(!ans) return NULL; size = _limb_multiply(self->limbs, self->ob_size, other->limbs, other->ob_size, ans->limbs); ans->ob_size = size; ans->limb_count = (size + LOG -1 )/LOG; done: if(shouldround) { decimalobject *fixed = _decimal_fix(ans, ctx); Py_DECREF(ans); return fixed; } return ans; } DECIMAL_SPECIAL_2FUNC(decimal_multiply) static decimalobject * _do_decimal_divide(decimalobject *self, decimalobject *other, contextobject *ctx) { return (decimalobject*)_do_decimal__divide(self, other, 0, ctx); } DECIMAL_SPECIAL_2FUNC(decimal_divide) static decimalobject * _do_decimal_floor_div(decimalobject *self, decimalobject *other, contextobject *ctx) { decimalobject *ret; PyObject *seq = _do_decimal__divide(self, other, 2, ctx); if (!seq) return NULL; if (PySequence_Check(seq)) { ret = (decimalobject*)PySequence_GetItem(seq, 0); Py_DECREF(seq); } else ret = (decimalobject*)seq; return ret; } DECIMAL_SPECIAL_2FUNC(decimal_floor_div) /* XXX what's the diffirence between div and truediv? =] */ static decimalobject * _do_decimal_true_div(decimalobject *self, decimalobject *other, contextobject *ctx) { /* XXX */ Py_RETURN_NONE; } DECIMAL_SPECIAL_2FUNC(decimal_true_div) static decimalobject * _do_decimal_remainder(decimalobject *self, decimalobject *other, contextobject *ctx) { decimalobject *ret; PyObject *seq; if (ISSPECIAL(self) || ISSPECIAL(other)) { decimalobject *nan; if (_check_nans(self, other, ctx, &nan)) return nan; } if (decimal_nonzero(self) && !decimal_nonzero(other)) { return handle_InvalidOperation(self->ob_type, ctx, "x % 0", NULL); } seq = _do_decimal__divide(self, other, 3, ctx); if (!seq) return NULL; if (PySequence_Check(seq)) { ret = (decimalobject*)PySequence_GetItem(seq, 1); Py_DECREF(seq); } else ret = (decimalobject*)seq; return ret; } DECIMAL_SPECIAL_2FUNC(decimal_remainder) static PyObject * _do_decimal_divmod(decimalobject *self, decimalobject *other, contextobject *ctx) { return _do_decimal__divide(self, other, 1, ctx); } DECIMAL_SPECIAL_2FUNC(decimal_divmod) static decimalobject * _do_decimal_power(decimalobject *self, decimalobject *other, decimalobject *modulo, contextobject *ctx) { decimalobject *ret=0, *val, *mul; long n; int sign; long spot; int mod = modulo != (decimalobject *)Py_None; long firstprec = ctx->prec; int cmp; int use_exp = 0; /* when we should use exp/ln method */ if (!ctx) ctx = getcontext(); if (!ctx) return NULL; if ((exp_g_i(ADJUSTED(other), 8)) && decimal_nonzero(other)) { use_exp = 1; } if(0) if (exp_eq_i(ADJUSTED(other), 9) && _limb_get_digit(other->limbs, other->ob_size, 0) >=2) use_exp = 1; if (ISSPECIAL(self) || ISSPECIAL(other)) { decimalobject *nan; if (_check_nans(self, other, ctx, &nan)) return nan; } if (ISINF(other)) { int cmp; if (!decimal_nonzero(self)) { if (other->sign &1) { return _NEW_decimalobj(1, SIGN_POSINF, exp_from_i(0)); } else { ret = _NEW_decimalobj(1, 0, exp_from_i(0)); if (!ret) return NULL; ret->limbs[0] = 0; return ret; } } if (self->sign &1) return handle_InvalidOperation(self->ob_type, ctx, "-x ** [+-]INF", NULL); { contextobject *ctx2; decimalobject *one; PyObject *flags; ctx2 = context_copy(ctx); if (!ctx2) return NULL; flags = context_ignore_all_flags(ctx2); if (!flags){ Py_DECREF(ctx2); return NULL; } Py_DECREF(flags); one = _NEW_decimalobj(1, 0, exp_from_i(0)); if (!one) { Py_DECREF(ctx2); return NULL; } one->limbs[0] = 1; cmp = _do_real_decimal_compare(self, one, ctx2); Py_DECREF(ctx2); Py_DECREF(one); if (PyErr_Occurred()) return NULL; } if (cmp == 0) { int i; if (handle_Inexact(ctx, NULL)) return NULL; if (handle_Rounded(ctx, NULL)) return NULL; ret = _NEW_decimalobj(ctx->prec, 0, exp_from_i(-ctx->prec+1)); if (!ret) return NULL; for (i=0;i limb_count;i++) ret->limbs[i] = 0; { long mult = 1; long where; long limb = (ctx->prec-1) / LOG; where = (ctx->prec-1) % LOG; while(where --) mult *= 10; ret->limbs[limb] = mult; } return ret; } /* cmp == 1 for self > 1 * cmp == 0 for self < 1*/ cmp += 1; cmp /= 2; /* if self > 1 self^inf = inf, self^-inf = 0 * if self < 1 self^inf = 0, self^-inf = inf */ /* inf */ if (cmp ^ (other->sign &1)) { return _NEW_decimalobj(1, SIGN_POSINF, exp_from_i(0)); } /* 0 */ else { ret = _NEW_decimalobj(1, 0, exp_from_i(0)); if (!ret) return NULL; ret->limbs[0] = 0; return ret; } } if (!_decimal_isint(other)) { use_exp = 1; } if (!decimal_nonzero(self) && !decimal_nonzero(other)) { return handle_InvalidOperation(self->ob_type, ctx, "0 ** 0", NULL); } if (!decimal_nonzero(other)) { ret = _NEW_decimalobj(1, 0, exp_from_i(0)); ret->limbs[0] = 1; return ret; } if (!use_exp) { PyObject *tmp = decimal_int(other); if (!tmp) return NULL; n = PyInt_AsLong(tmp); Py_DECREF(tmp); if (PyErr_Occurred()) { return NULL; } } /* all we really need here is n%2 - for special cases */ else { /* where = other->ob_size - other->exp - 1 */ exp_t where = exp_from_i(other->ob_size); exp_inp_sub(&where, exp_add_i(other->exp, 1)); n = _limb_get_digit(other->limbs, other->ob_size, exp_to_i(where)); } sign = (self->sign&1) && n&1; if (!decimal_nonzero(self)) { if (other->sign &1) { ret = _NEW_decimalobj(1, 0, exp_from_i(0)); if (!ret) return NULL; ret->sign = sign ? SIGN_NEGINF : SIGN_POSINF; return ret; } else { ret = _NEW_decimalobj(1, sign, exp_from_i(0)); if (!ret) return NULL; ret->limbs[0] = 0; return ret; } } if (ISINF(self)) { if (mod) { return handle_InvalidOperation(self->ob_type, ctx, "INF % x", NULL); } if (!_decimal_isint(other) && self->sign&1) return handle_InvalidOperation(self->ob_type, ctx, "INF ** -(non-int)", NULL); ret = _NEW_decimalobj(1, sign, exp_from_i(0)); ret->limbs[0] = 0; if (decimal_nonzero(other) && !(other->sign &1)) ret->sign = sign ? SIGN_NEGINF : SIGN_POSINF; return ret; } /* non-integer case */ /* we calculate it using exp(ln(self) * other) */ if (use_exp) { decimalobject *tmp; contextobject *ctx2; /* check context */ if (!check_ctx(self, ctx)) return handle_InvalidContext(self->ob_type, ctx, NULL); /* now check operands */ if (decimal_nonzero(self) && ( self->ob_size > MAX_MATH || exp_g_i(exp_add_i(self->exp, self->ob_size), MAX_MATH + 1) || exp_l_i(exp_add_i(self->exp, self->ob_size), 2 * (1 - MAX_MATH)))) return handle_InvalidOperation(self->ob_type, ctx, "Operand range violation", NULL); if (decimal_nonzero(other) && ( other->ob_size > MAX_MATH || exp_g_i(exp_add_i(other->exp, other->ob_size), MAX_MATH + 1) || exp_l_i(exp_add_i(other->exp, other->ob_size), 2 * (1 - MAX_MATH)))) return handle_InvalidOperation(other->ob_type, ctx, "Operand range violation", NULL); /* special cases */ { int i; int special = 1; if (!exp_eq_i(ADJUSTED(self), -1)) special = 0; if (special) for (i = 0 ; i < ctx->prec; i ++) { if (_limb_get_digit(self->limbs, self->ob_size, i) != 9) { special = 0; break; } } /* self is 0.999...99 */ if (special) { /* 0 < other < 1 */ if (!(other->sign&1) && exp_le_i(ADJUSTED(other), -1) && decimal_nonzero(other)) { /* if round up then return 1 */ if (ctx->rounding == ROUND_UP || ctx->rounding == ROUND_CEILING) { long limb = ctx->prec / LOG; long mul = (ctx->prec % LOG) - 1; if (handle_Inexact(ctx, NULL)) return NULL; if (handle_Rounded(ctx, NULL)) return NULL; ret = _NEW_decimalobj(ctx->prec, 0, exp_from_i(1 - ctx->prec)); if (!ret) return NULL; for (i=0 ; i < ret->limb_count ;i ++) ret->limbs[i] = 0; ret->limbs[limb] = 1; for (i = 0 ; i < mul ; i ++) ret->limbs[limb] *= 10; return ret; } /* if round down then return self */ else if (ctx->rounding == ROUND_DOWN || ctx->rounding == ROUND_FLOOR){ if (handle_Inexact(ctx, NULL)) return NULL; if (handle_Rounded(ctx, NULL)) return NULL; return _decimal_get_copy(self); } } } } ctx2 = context_shallow_copy(ctx); if (!ctx2) return NULL; ctx2->Emax = exp_from_i(MAX_MATH); ctx2->Emin = exp_from_i(-MAX_MATH); ctx2->clamp = 0; ctx2->rounding = ctx->rounding; ctx2->rounding_dec = ALWAYS_ROUND; /* we take context precision or size of self if greater and * add some constant */ ctx2->prec = ctx->prec > self->ob_size ? ctx->prec : self->ob_size; ctx2->prec += 14; ret = _do_decimal__ln(self, ctx2); if (!ret) { Py_DECREF(ctx2); return NULL; } if (!decimal_nonzero(ret)) { int i; Py_DECREF(ret); Py_DECREF(ctx2); if (!_decimal_isint(other)) { ret = _NEW_decimalobj(ctx->prec, 0, exp_from_i(-ctx->prec + 1)); if (!ret) return NULL; if (handle_Inexact(ctx, NULL)) return NULL; if (handle_Rounded(ctx, NULL)) return NULL; for (i = 0; i < ret -> limb_count; i++) { ret->limbs[i] = 0; } { long mult = 1; long limb; long where = (ctx->prec - 1)% LOG; limb = (ctx->prec - 1)/LOG; while(where--) mult *= 10; ret->limbs[limb] = mult; } return ret; } else { ret = _NEW_decimalobj(1, 0, exp_from_i(0)); if (!ret) return NULL; ret->limbs[0] = 1; return ret; } } tmp = _do_decimal_multiply(ret, other, ctx2); Py_DECREF(ret); if (!tmp) { Py_DECREF(ctx2); return NULL; } ret = tmp; tmp = _do_decimal_exponent(ret, ctx2); Py_DECREF(ret); Py_DECREF(ctx2); if (!tmp) return NULL; ret = tmp; if (ctx->rounding_dec == ALWAYS_ROUND) { tmp = _decimal_fix(ret, ctx); Py_DECREF(ret); if (!tmp) return NULL; ret = tmp; } return ret; } /* XXX temporary solution */ { #ifdef BIG_EXP exp_t b; exp_t a = exp_add_i(self->exp, self->ob_size - 1); b = exp_mul_i(a, n); cmp = exp_g(a, ctx->Emax); #else double a,b; a = ((double) self->exp + (double) self->ob_size - (double) 1) * (double) n; b = ctx->Emax; cmp = a > b; #endif } if (!mod && n > 0 && /* (self->exp + self->ob_size -1) * n > ctx->Emax && */ cmp && decimal_nonzero(self)) { if (handle_Rounded(ctx, NULL)) { return NULL; } if (handle_Inexact(ctx, NULL)) { return NULL; } return handle_Overflow(self->ob_type, ctx, "Big power", sign); } firstprec = ctx->prec; ctx->prec += 2; { long t = n; if(!t) ctx->prec += 1; else while(t) { ctx->prec += 1; t/=10; } } /* TODO shouldn't it be Invalid_Context ? */ if (!mod && exp_l_i(PyDecimal_DefaultContext->Emax, ctx->prec)) { ctx->prec = firstprec; return handle_Overflow(self->ob_type, ctx, "Too much precision", sign); } mul = _decimal_get_copy(self); val = _NEW_decimalobj(1,0,exp_from_i(0)); val->limbs[0] = 1; if(!val || !mul) { Py_XDECREF(mul); Py_XDECREF(val); ctx->prec = firstprec; return NULL; } if (n < 0) { decimalobject *tmp; n *= -1; tmp = (decimalobject*)_do_decimal__divide(val, mul, 0, ctx); if (!tmp) { Py_DECREF(mul); Py_DECREF(val); ctx->prec = firstprec; return NULL; } Py_DECREF(mul); mul = tmp; } spot = 1; while (spot <= n) { spot <<= 1; } spot >>= 1; while (spot) { decimalobject *tmp; tmp = _do_decimal_multiply(val, val, ctx); if (!tmp) goto err; Py_DECREF(val); val = tmp; if (spot & n) { tmp = _do_decimal_multiply(val, mul, ctx); if (!tmp) goto err; Py_DECREF(val); val = tmp; } if (mod) { tmp = _do_decimal_remainder(val, modulo, ctx); if (!tmp) goto err; Py_DECREF(val); val = tmp; } spot >>= 1; } ctx->prec = firstprec; if (ctx->rounding_dec == ALWAYS_ROUND) { ret = _decimal_fix(val, ctx); if (!ret) goto err; } else { ret = val; Py_INCREF(val); } Py_DECREF(val); Py_DECREF(mul); return ret; err: ctx->prec = firstprec; Py_DECREF(val); Py_DECREF(mul); Py_XDECREF(ret); return NULL; } static PyObject * decimal_power(PyObject *self, PyObject *other, PyObject *modulo) { decimalobject *res; decimalobject *dec; contextobject *ctx = getcontext(); if (!ctx) return NULL; /* other = _convert_to_decimal(self->ob_type, other, ctx, 0); if (other == NULL || other == Py_NotImplemented) return other; */ if (PyDecimal_Check(self)) { other = _convert_to_decimal(self->ob_type, other, ctx, 0); if (!other || other == Py_NotImplemented) return other; dec = (decimalobject*)other; } else { assert(PyDecimal_Check(other)); self = _convert_to_decimal(other->ob_type, self, ctx, 0); if (!self || self == Py_NotImplemented) return self; dec = (decimalobject*)self; } if (modulo == Py_None) { Py_INCREF(modulo); } else { modulo = _convert_to_decimal(self->ob_type, modulo, ctx, 0); if (modulo == NULL || modulo == Py_NotImplemented) { Py_DECREF(other); return modulo; } } res = _do_decimal_power((decimalobject *)self, (decimalobject *)other, (decimalobject *)modulo, ctx); Py_DECREF(dec); Py_DECREF(modulo); return (PyObject *)res; } static decimalobject * _do_decimal_negative(decimalobject *self, contextobject *ctx) { int sign = self->sign; decimalobject *tmp, *res; if (ISSPECIAL(self)) { decimalobject *nan; if (_check_nans(self, NULL, ctx, &nan) != 0) return nan; } if (! decimal_nonzero(self)) /* - '0' => '0', not '-0' */ sign = 0; else if (self->sign & 1) sign--; else sign++; res = _decimal_get_copy(self); if (!res) return NULL; res->sign = sign; if (ctx->rounding_dec == ALWAYS_ROUND) { tmp = _decimal_fix(res, ctx); Py_DECREF(res); if (!tmp) return NULL; return tmp; } else { return res; } } DECIMAL_SPECIAL_1FUNC(decimal_negative) static decimalobject * _do_decimal_positive(decimalobject *self, contextobject *ctx) { decimalobject *res, *tmp; char sign = self->sign; if (ISSPECIAL(self)) { decimalobject *nan; if (_check_nans(self, NULL, ctx, &nan) != 0) return nan; } if (! decimal_nonzero(self)) /* + '-0' => '0' */ sign = 0; res = _decimal_get_copy(self); if (!res) return NULL; res->sign = sign; if (ctx->rounding_dec == ALWAYS_ROUND) { tmp = _decimal_fix(res, ctx); Py_DECREF(res); if (!tmp) return NULL; return tmp; } else { return res; } } DECIMAL_SPECIAL_1FUNC(decimal_positive) static decimalobject * _do_decimal_absolute(decimalobject *self, contextobject *ctx, int round) { contextobject *ctx2 = NULL; decimalobject *ans; if (ISSPECIAL(self)) { decimalobject *nan; if (_check_nans(self, NULL, ctx, &nan) != 0) return nan; } if (!round) { ctx2 = context_copy(ctx); if (!ctx2) return NULL; ctx2->rounding_dec = NEVER_ROUND; ctx = ctx2; } if (self->sign & 1) ans = _do_decimal_negative(self, ctx); else ans = _do_decimal_positive(self, ctx); Py_XDECREF(ctx2); return ans; } static PyObject * decimal_absolute(PyObject *self) { contextobject *ctx = getcontext(); if (!ctx) return NULL; return (PyObject *)_do_decimal_absolute((decimalobject *)self, ctx, 1); } static PyObject * decimal_long(decimalobject *self) { long i, max; char *buf; PyObject *res; max = exp_to_i(self->exp) + self->ob_size; buf = PyMem_MALLOC(max + 2); /* with sign */ if (!buf) return NULL; buf[0] = (self->sign & 1 ? '-' : '+'); for (i = 0; i < max; i++) { if (i < self->ob_size) buf[i+1] = _limb_get_digit(self->limbs, self->ob_size, i) + '0'; /* SLOW */ else buf[i+1] = '0'; } buf[max+1] = 0; res = PyLong_FromString(buf, NULL, 10); PyMem_FREE(buf); return res; } /* Convert to int, truncating. */ static PyObject * decimal_int(decimalobject *self) { long res = 0; long i, max; if (ISSPECIAL(self)) { if (GETNAN(self)) { contextobject *ctx; ctx = getcontext(); if (!ctx) return NULL; return (PyObject *)handle_InvalidContext(self->ob_type, ctx, NULL); } else if (ISINF(self)) { PyErr_SetString(PyExc_OverflowError, "Cannot convert infinity to long"); return NULL; } } /* try converting to int, if it's too big, convert to long */ max = self->ob_size + exp_to_i(self->exp); #if SIZEOF_LONG == 4 if (max > 9) return decimal_long(self); #elif SIZEOF_LONG == 8 if (max > 18) return decimal_long(self); #else #error "Decimal currently assumes that SIZEOF_LONG is 4 or 8, please adapt this" #endif for (i = 0; i < max; i++) { if (i < self->ob_size) res = res * 10 + _limb_get_digit(self->limbs, self->ob_size, i); /* SLOW */ else res *= 10; } if (self->sign & 1) res = -res; return PyInt_FromLong(res); } static PyObject * decimal_float(decimalobject *self) { PyObject *str, *res; str = decimal_str(self); if (!str) return NULL; res = PyFloat_FromString(str, NULL); Py_DECREF(str); return res; } static int decimal_nonzero(decimalobject *self) { long i; if (ISSPECIAL(self)) return 1; for(i=0;i limb_count;i++) if(self->limbs[i] != 0) return 1; return 0; } /* Trying to avoid casting if not necessary prevents wrong function signatures. */ static PyNumberMethods decimal_as_number = { decimal_add, /* nb_add */ decimal_subtract, /* nb_subtract */ decimal_multiply, /* nb_multiply */ decimal_divide, /* nb_divide */ decimal_remainder, /* nb_remainder */ decimal_divmod, /* nb_divmod */ decimal_power, /* nb_power */ decimal_negative, /* nb_negative */ decimal_positive, /* nb_positive */ decimal_absolute, /* nb_absolute */ (inquiry)decimal_nonzero, /* nb_nonzero */ 0, /* nb_invert */ 0, /* nb_lshift */ 0, /* nb_rshift */ 0, /* nb_and */ 0, /* nb_xor */ 0, /* nb_or */ 0, /* nb_coerce */ (unaryfunc)decimal_int, /* nb_int */ (unaryfunc)decimal_long, /* nb_long */ (unaryfunc)decimal_float, /* nb_float */ 0, /* nb_oct */ 0, /* nb_hex */ 0, /* nb_inplace_add */ 0, /* nb_inplace_subtract */ 0, /* nb_inplace_multiply */ 0, /* nb_inplace_divide */ 0, /* nb_inplace_remainder */ 0, /* nb_inplace_power */ 0, /* nb_inplace_lshift */ 0, /* nb_inplace_rshift */ 0, /* nb_inplace_and */ 0, /* nb_inplace_xor */ 0, /* nb_inplace_or */ decimal_floor_div, /* nb_floor_divide */ decimal_true_div, /* nb_true_divide */ 0, /* nb_inplace_floor_divide */ 0, /* nb_inplace_true_divide */ 0, /* nb_index */ }; int _decimal_isint(decimalobject *d) { exp_t i; exp_t cmp; if (exp_ge_i(d->exp, 0)) /* if the exponent is positive, there's no * fractional part at all. */ return 1; /* Here comes the tricky part. We have to find out * whether the fractional part consists only of zeroes. */ i = exp_from_i(d->ob_size-1); cmp = exp_add_i(d->exp, d->ob_size); /* TODO bail out when we go out of bound */ for (; exp_ge(i, cmp); exp_dec(&i)) { /* if (d->digits[i] > 0)*/ if(_limb_get_digit(d->limbs, d->ob_size, exp_to_i(i)) > 0) return 0; } return 1; } static void decimal_dealloc(decimalobject *d) { PyObject_FREE(d->limbs); d->ob_type->tp_free(d); } static decimalobject * _decimal_fromliteralnan(PyTypeObject *type, char *str, long len, contextobject *ctx) { char literalsign = 0, sign = 0; decimalobject *new; long size = 0, i; char *start = NULL, *p; long mult; long limb; if (len < 3) 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' && tolower(str[2]) == 'n') { sign = (literalsign ? SIGN_NEGNAN : SIGN_POSNAN); if (len < 4) goto finish; p = str+3; } else if (tolower(str[0]) == 's' && tolower(str[1]) == 'n' && tolower(str[2]) == 'a' && tolower(str[3]) == 'n') { sign = (literalsign ? SIGN_NEGSNAN : SIGN_POSSNAN); if (len < 5) goto finish; p = str+4; } else { return NULL; } /* diagnostic info: skip leading zeroes */ while (*p == '0') p++; start = p; while (isdigit(*p++)) size++; if (p == str+len+1) goto finish; return NULL; finish: /* 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) return NULL; for(i=0;i limb_count;i++) new->limbs[i] = 0; mult = 1; 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; } /* Possible spellings for Infinity. (The first character decides the sign) */ static char *infinities[] = { "inf", "infinity", "+inf", "+infinity", "-inf", "-infinity", 0 }; /* Decimal constructing functions. */ static decimalobject * _decimal_fromliteralinfinity(PyTypeObject *type, char *str) { decimalobject *new; long i; char **p = infinities; char sign = 0; char *str2; str2 = (char*)malloc(strlen(str) + 1); if (!str2) { PyErr_NoMemory(); return NULL; } for (i = 0 ; i < strlen(str) ; i++) { str2[i] = tolower(str[i]); } str2[strlen(str)] = '\0'; do { if (strcmp(str2, *p) == 0) { if (str2[0] == '-') sign = SIGN_NEGINF; else sign = SIGN_POSINF; break; } } while (*++p); if (sign) { new = _new_decimalobj(type, 1, sign, exp_from_i(0)); if (!new) return NULL; new->limbs[0] = 0; free(str2); return new; } free(str2); return NULL; } static decimalobject * _decimal_fromliteral(PyTypeObject *type, char *str, long len, contextobject *ctx) { char sign = 0; exp_t exp = exp_from_i(0); long ndigits; long zero = 0; /* if zero */ long digits_after_dot = 0; char *p = str; char *first_digit = 0; char *last_digit = 0; char *dot = 0; char *e = 0; int any_digit = 0; char *c; long i; long mult, limb; decimalobject *new; if(!len) goto err; /* empty string */ if(*p == '+') p++; else if(*p == '-') { p++; sign = 1; } if(*p == 'e' || *p == 'E') /* no digits before E */ goto err; /* now at *p is our integer with (possibly) dot */ for(c = p; c - str < len; c++) { if(!isdigit(*c)) { if(*c == 'e' || *c == 'E') { if(e) goto err; /* we should have only one e */ e = c; } else if(*c == '.') { if(dot || e) goto err; dot = c; } else if(*c == '-' || *c == '+') /* we should have sign only after e */ { if(c - e != 1) goto err; } else /* we shouldn't have anything else */ goto err; } else { if(!e) any_digit = 1; if(dot && !e) digits_after_dot ++; if(!first_digit && *c != '0') { if(!e) /* it's digit of exponent */ first_digit = c; } } } /* we now are pretty sure, that string is properly formated */ if(!any_digit) goto err; if(!first_digit) { zero = 1; ndigits = 1; } else /* let's count digits and find by the way last digit*/ { ndigits = 0; for(c = first_digit; c - str < len; c++) { if(isdigit(*c)) { ndigits ++; last_digit = c; } else if(*c == 'e' || *c == 'E') break; assert(isdigit(*c) || *c == '.'); } } if(!ndigits) { goto err; } if(e) /* pretty obvious */ { int ok; ok = exp_sscanf(e+1, &exp); if(ok!=1) { goto err; } } exp_inp_sub_i(&exp, digits_after_dot); new = _new_decimalobj(type, ndigits, sign, exp); if(!new) return NULL; for(i = 0; i limb_count ;i++) new->limbs[i] = 0; if(!first_digit) /* we are 0 */ { new->limbs[0] = 0; return new; } mult = 1; /* now we just read integer till '\0' or 'e'/'E', dont care about '.' */ limb = 0; for(c = last_digit; c >= first_digit; c--) { if(*c == 'e' || *c == 'E') break; if(!isdigit(*c)) continue; assert(limb < new->limb_count); new->limbs[limb] += mult * (*c - '0'); mult *= 10; if(mult == BASE) { limb ++; mult = 1; } } return new; err: return handle_ConversionSyntax(type, ctx, "invalid literal for Decimal"); } static PyObject * decimal_from_string(PyTypeObject *type, char *buffer, long buf_len, contextobject *ctx) { decimalobject *new; new = _decimal_fromliteralinfinity(type, buffer); if (new) return (PyObject *)new; if (PyErr_Occurred()) return NULL; new = _decimal_fromliteralnan(type, buffer, buf_len, ctx); if (new) return (PyObject *)new; if (PyErr_Occurred()) return NULL; return (PyObject *)_decimal_fromliteral(type, buffer, buf_len, ctx); } PyObject * PyDecimal_FromString(char *buffer, long buf_len, contextobject *ctx) { return decimal_from_string(&PyDecimal_DecimalType, buffer, buf_len, ctx); } static PyObject * decimal_from_long(PyTypeObject *type, long value) { decimalobject *new; long v; int ndigits = 0, neg = 0, i = 0; if (value == 0) { new = _new_decimalobj(type, 1, 0, exp_from_i(0)); if (!new) return NULL; new->limbs[0] = 0; return (PyObject *)new; } else if (value < 0) { value = -value; neg = 1; } v = value; while (v) { ndigits++; v /= 10; } v = value; new = _new_decimalobj(type, ndigits, (neg ? SIGN_NEG : SIGN_POS), exp_from_i(0)); if (!new) return NULL; for(i=0; i < new->limb_count; i++) { new->limbs[i] = v % BASE; v /= BASE; } return (PyObject *)new; } PyObject * PyDecimal_FromLong(long value) { return decimal_from_long(&PyDecimal_DecimalType, value); } /* convert from a 3-tuple of (sign, digits, exp) */ static PyObject * decimal_from_sequence(PyTypeObject *type, PyObject *seq) { decimalobject *new = NULL; PyObject *tup, *digits, *digtup = NULL, *item; int sign; long i; long mult, limb; exp_t exp; PyObject *tmp_exp; if (PyTuple_Check(seq)) { tup = seq; Py_INCREF(tup); } else { tup = PySequence_Tuple(seq); if (!tup) goto err; } if (PyTuple_GET_SIZE(tup) != 3) { PyErr_SetString(PyExc_ValueError, "Invalid arguments"); goto err; } if (!PyArg_ParseTuple(tup, "iOO", &sign, &digits, &tmp_exp)) goto err; exp = exp_from_pyobj(tmp_exp); if (PyErr_Occurred()) goto err; if (sign < 0 || sign > 7) { PyErr_SetString(PyExc_ValueError, "Invalid sign"); goto err; } digtup = PySequence_Tuple(digits); if (!digtup) { PyErr_SetString(PyExc_ValueError, "Invalid arguments"); goto err; } new = _new_decimalobj(type, PyTuple_GET_SIZE(digtup), sign, exp); for(i = 0;i < new->limb_count;i++) new->limbs[i] = 0; mult = 1; limb = 0; new->limbs[0] = 0; for(i = new->ob_size-1; i>=0; i--) /* limb[0] keeps least significant limb */ { long x; item = PyTuple_GET_ITEM(digtup, i); if(PyInt_Check(item)) x = PyInt_AsLong(item); else if (PyLong_Check(item)) { x = PyLong_AsLong(item); if(x == -1 && PyErr_Occurred()) goto err; } else { PyErr_SetString(PyExc_ValueError, "The second value in the tuple " "must be composed of non negative integer elements."); goto err; } if(x < 0 || x > 9) { PyErr_Format(PyExc_ValueError, "Invalid digit: %ld", x); goto err; } assert(limb < new->limb_count); new->limbs[limb] += mult * x; mult *= 10; if(mult == BASE) /* we already used LOG digits of one limb */ { mult = 1; limb ++; } } Py_DECREF(digtup); Py_DECREF(tup); return (PyObject *)new; err: Py_XDECREF(digtup); Py_DECREF(tup); Py_XDECREF(new); return NULL; } PyObject * PyDecimal_FromSequence(PyObject *seq) { return decimal_from_sequence(&PyDecimal_DecimalType, seq); } static decimalobject * _decimal_from_pylong(PyTypeObject *type, PyObject *val, contextobject *ctx) { char *buffer; Py_ssize_t buf_len = 0; PyObject *strval; decimalobject *res; strval = PyObject_Str(val); if (!strval) return NULL; if (PyObject_AsCharBuffer(strval, (const char **)&buffer, &buf_len) == -1) { Py_DECREF(strval); return NULL; } if (buf_len > LONG_MAX) { PyErr_NoMemory(); return NULL; } res = (decimalobject *)decimal_from_string(type, buffer, buf_len, ctx); Py_DECREF(strval); return res; } static PyObject * decimal_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"value", "context", 0}; PyObject *value = NULL; PyObject *context = NULL; PyObject *repr; contextobject *ctx; char *buffer; Py_ssize_t buf_len = 0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO:Decimal", kwlist, &value, &context)) return NULL; if (value == NULL) { decimalobject *new = _new_decimalobj(type, 1, 0, exp_from_i(0)); if (!new) return NULL; new->limbs[0] = 0; return (PyObject *)new; } if(context) if(!PyDecimalContext_Check(context)) { PyErr_SetString(PyExc_TypeError, "context must be Context type"); return NULL; } if (PyDecimal_Check(value)) return (PyObject *)_decimal_get_copy((decimalobject *)value); if (PyFloat_Check(value)) { PyErr_SetString(PyExc_TypeError, "Cannot convert float to Decimal. " "First convert the float to a string."); return NULL; } if (PyList_Check(value) || PyTuple_Check(value)) return decimal_from_sequence(type, value); if (PyInt_Check(value)) { long x = PyInt_AsLong(value); if (x == -1 && PyErr_Occurred()) return NULL; return decimal_from_long(type, x); } if(context) ctx = (contextobject*) context; else ctx = getcontext(); if (!ctx) return NULL; if (PyLong_Check(value)) return (PyObject *)_decimal_from_pylong(type, value, ctx); /* try buffer interface (e.g. strings and unicode) */ if (PyObject_AsCharBuffer(value, (const char **)&buffer, &buf_len) == 0) { PyObject *res; if (buf_len > LONG_MAX) { PyErr_NoMemory(); return NULL; } res = (PyObject *)decimal_from_string(type, buffer, buf_len, ctx); return res; } /* if PyObject_AsCharBuffer didn't set a TypeError, fail immediately */ if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError)) return NULL; /* no suitable type found */ repr = PyObject_Repr(value); if (!repr) return NULL; if (!PyString_Check(repr)) PyErr_SetString(PyExc_TypeError, "Cannot convert object to Decimal"); else PyErr_Format(PyExc_TypeError, "Cannot convert %s to Decimal", PyString_AS_STRING(repr)); Py_DECREF(repr); return NULL; } static PyObject * decimal_repr(decimalobject *d) { PyObject *str, *res; static PyObject *format; if (!format) { format = PyString_FromString("Decimal(\"%s\")"); } str = decimal_str(d); if (!str) return NULL; res = PyString_Format(format, str); Py_DECREF(str); return res; } static long decimal_hash(decimalobject *d) { PyObject *tmp, *tmp2; long hash; if (ISSPECIAL(d)) { if (GETNAN(d)) { PyErr_SetString(PyExc_TypeError, "Cannot hash a NaN value."); return -1; } tmp = decimal_str(d); if (!tmp) return -1; hash = PyObject_Hash(tmp); Py_DECREF(tmp); return hash; } if (_decimal_isint(d)) { tmp = decimal_int(d); if (!tmp) return -1; hash = PyObject_Hash(tmp); Py_DECREF(tmp); return hash; } tmp2 = PyTuple_New(0); if (!tmp2) return -1; /* This is not calling _do_decimal_normalize because we're having no Context ready. */ tmp = (PyObject *)decimal_normalize(d, tmp2, NULL); Py_DECREF(tmp2); if (!tmp) return -1; tmp2 = decimal_str((decimalobject *)tmp); Py_DECREF(tmp); if (!tmp2) return -1; hash = PyObject_Hash(tmp2); Py_DECREF(tmp2); return hash; } /* XXX: just for debugging, or should we keep those? */ 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} }; static PyObject * _decimal_get_int(decimalobject *self) { PyObject *tup; long i; tup = PyTuple_New(self->ob_size); if (!tup) return NULL; for (i = 0; i < self->ob_size; i++) PyTuple_SET_ITEM(tup, i, PyInt_FromLong(_limb_get_digit(self->limbs, self->ob_size, i))); /* SLOW */ return tup; } static int _decimal_set_int(decimalobject *self, PyObject *value) { long i, size, val; long mul, limb; long *limbs; PyObject *item; if (!PyTuple_Check(value)) { PyErr_SetString(PyExc_TypeError, "_int must be a tuple"); return -1; } size = PyTuple_GET_SIZE(value); limbs = PyObject_MALLOC(((size + LOG -1)/LOG) * sizeof(long)); if (!limbs) { PyErr_NoMemory(); return -1; } mul = 1; limb = 0; limbs[0] = 0; for (i = size-1; i >= 0; i--) { item = PyTuple_GET_ITEM(value, i); if (!PyInt_Check(item)) { PyObject_FREE(limbs); PyErr_SetString(PyExc_TypeError, "_int must consist of ints"); return -1; } val = PyInt_AsLong(item); if (val < 0 || val > 9) { PyObject_FREE(limbs); PyErr_SetString(PyExc_TypeError, "_int digits must be 0-9"); return -1; } limbs[limb] += mul * val; mul *= 10; if (mul == BASE) { limb ++; mul = 1; if (i > 0) limbs[limb] = 0; } } PyObject_FREE(self->limbs); self->ob_size = size; self->limb_count = (size + LOG-1)/LOG; self->limbs = limbs; return 0; } static PyObject * _decimal_get_limbs(decimalobject *self) { PyObject *tup; long i; tup = PyTuple_New(self->limb_count); if(!tup) return NULL; for(i = 0; i< self->limb_count; i++) PyTuple_SET_ITEM(tup, i, PyInt_FromLong(self->limbs[i])); return tup; } static PyObject * _decimal_is_special(decimalobject *self) { if (ISSPECIAL(self)) Py_RETURN_TRUE; else Py_RETURN_FALSE; } static PyGetSetDef _decimal_getset[] = { {"_limbs", (getter)_decimal_get_limbs, 0}, {"_int", (getter)_decimal_get_int, (setter)_decimal_set_int}, {"_is_special", (getter)_decimal_is_special, 0}, {NULL} }; static PyTypeObject PyDecimal_DecimalType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ MODULE_NAME ".Decimal", /* tp_name */ sizeof(decimalobject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)decimal_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ (cmpfunc)decimal_cmp, /* tp_compare */ (reprfunc)decimal_repr, /* tp_repr */ &decimal_as_number, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ (hashfunc)decimal_hash, /* tp_hash */ 0, /* tp_call */ (reprfunc)decimal_str, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_BASETYPE, /* tp_flags */ decimal_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ (richcmpfunc)decimal_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ decimal_methods, /* tp_methods */ _decimal_members, /* tp_members */ _decimal_getset, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ decimal_new, /* tp_new */ PyObject_Del, /* tp_free */ }; /* Context threading magic ***************************************************/ #ifdef WITH_THREAD /* XXX: No idea if it's actually that easy to get a thread-local context object working. I intended to let Tim Peters double-check this, but now it's your turn ;) */ /* Get the context for the current thread. This returns a borrowed reference since we don't have to DECREF it in about every function then. */ static contextobject * getcontext(void) { PyObject *tdict; contextobject *ctx; tdict = PyThreadState_GetDict(); if (!tdict) { PyErr_SetString(PyExc_SystemError, "Couldn't get thread-state dictionary"); return NULL; } ctx = (contextobject *)PyDict_GetItemString(tdict, MODULE_NAME ".context"); if (!ctx) { PyObject *args; args = PyTuple_New(0); if (!args) return NULL; ctx = (contextobject *)context_new(&PyDecimal_DecimalContextType, args, NULL); Py_DECREF(args); if (!ctx) return NULL; if (PyDict_SetItemString(tdict, MODULE_NAME ".context", (PyObject *)ctx) < 0) { Py_DECREF(ctx); return NULL; } Py_DECREF(ctx); /* the dict now has a reference, see comment below */ return ctx; } /* ctx is NOT incref'd since that simplifies argument parsing * in functions that either are passed a context or use the default */ return ctx; } static int setcontext(contextobject *ctx) { PyObject *tdict; tdict = PyThreadState_GetDict(); if (!tdict) { PyErr_SetString(PyExc_SystemError, "Couldn't get thread-state dictionary"); return -1; } if (PyDict_SetItemString(tdict, MODULE_NAME ".context", (PyObject *)ctx) < 0) return -1; return 0; } #else /* WITH_THREAD */ static PyObject *current_context; /* Get the global current context object. Returns a borrowed reference. */ static contextobject * getcontext() { if (current_context == NULL) { PyObject *args; args = PyTuple_New(0); if (!args) return NULL; current_context = context_new(&PyDecimal_DecimalContextType, args, NULL); Py_DECREF(args); if (!current_context) return NULL; } /* no Py_INCREF here */ return (contextobject *)current_context; } static int setcontext(contextobject *ctx) { Py_CLEAR(current_context); Py_INCREF(ctx); current_context = ctx; return 0; } #endif /* WITH_THREAD */ /* Context object ************************************************************/ static contextobject * _new_contextobj(PyTypeObject *type, long prec, int rounding, int rounding_dec, PyObject *traps, PyObject *flags, exp_t Emin, exp_t Emax, int capitals, int clamp, PyObject *ignored, int copy_dicts) { contextobject *new; PyObject *f = NULL, *t = NULL, *i = NULL; /* new = (contextobject *)PyObject_NEW(contextobject, &PyDecimal_DecimalContextType); */ new = (contextobject*) type->tp_alloc(type, 0); if (new == NULL) return NULL; if (!flags) { f = PyDict_New(); } else if (copy_dicts) { f = PyDict_Copy(flags); } else { f = flags; Py_INCREF(f); } if (!f) goto err; if (!traps) { t = PyDict_New(); } else if (copy_dicts) { t = PyDict_Copy(traps); } else { t = traps; Py_INCREF(t); } if (!t) goto err; if (!ignored) { i = PyDict_New(); } else if (copy_dicts) { i = PyDict_Copy(ignored); } else { i = ignored; Py_INCREF(i); } if (!i) goto err; new->flags = f; new->traps = t; new->ignored = i; new->prec = prec; new->rounding = rounding; new->rounding_dec = rounding_dec; new->Emin = Emin; new->Emax = Emax; new->capitals = capitals; new->clamp = clamp; return new; err: Py_XDECREF(f); Py_XDECREF(t); Py_XDECREF(i); return NULL; } /* Context methods ************************************************************/ static PyObject * context_clear_flags(contextobject *self) { int i; for(i = 0;i flags,i,0); Py_RETURN_NONE; } static contextobject * context_shallow_copy(contextobject *ctx) { return _new_contextobj(ctx->ob_type, ctx->prec, ctx->rounding, ctx->rounding_dec, ctx->traps, ctx->flags, ctx->Emin, ctx->Emax, ctx->capitals, ctx->clamp, ctx->ignored, 0); } static contextobject * context_copy(contextobject *ctx) { return _new_contextobj(ctx->ob_type, ctx->prec, ctx->rounding, ctx->rounding_dec, ctx->traps, ctx->flags, ctx->Emin, ctx->Emax, ctx->capitals, ctx->clamp, ctx->ignored, 1); /* Note the difference: ^ */ } static decimalobject * context_create_decimal(contextobject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"num", 0}; PyObject *thing = NULL; PyObject *nargs, *nkwds, *res; decimalobject *fixed; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &thing)) return NULL; if (thing == NULL) { nargs = PyTuple_New(0); if (!nargs) return NULL; nkwds = PyDict_New(); if (!nkwds) { Py_DECREF(nargs); return NULL; } if (PyDict_SetItemString(nkwds, "context", (PyObject *)self) < 0) { Py_DECREF(nargs); Py_DECREF(nkwds); return NULL; } res = decimal_new(&PyDecimal_DecimalType, nargs, nkwds); Py_DECREF(nargs); Py_DECREF(nkwds); } else { nargs = PyTuple_Pack(2, thing, self); if (!nargs) return NULL; res = decimal_new(&PyDecimal_DecimalType, nargs, NULL); Py_DECREF(nargs); } if (!res) return NULL; fixed = _decimal_fix((decimalobject *)res, self); Py_DECREF(res); return fixed; } static PyObject * context_Etiny(contextobject *self) { return exp_to_pyobj(ETINY(self)); } static PyObject * context_Etop(contextobject *self) { return exp_to_pyobj(ETOP(self)); } /* Define context functions that just call Decimal methods with a context argument of self. */ #define CONTEXT_UNARY_FUNC(name, decname) \ static PyObject * \ context_##name(contextobject *self, PyObject *a) { \ decimalobject *dec_a = NULL; \ PyObject *res; \ dec_a = (decimalobject *)_convert_to_decimal( \ &PyDecimal_DecimalType, a, self, 1); \ if (dec_a == NULL) return NULL; \ res = (PyObject *)_do_decimal_##decname(dec_a, self); \ Py_DECREF(dec_a); \ return res; \ } #define CONTEXT_BINARY_FUNC(name, decname) \ static PyObject * \ context_##name(contextobject *self, PyObject *args) { \ PyObject *a, *b, *res; \ decimalobject *dec_a = NULL, *dec_b = NULL; \ if (!PyArg_ParseTuple(args, "OO:" #name, &a, &b)) return NULL; \ dec_a = (decimalobject *)_convert_to_decimal( \ &PyDecimal_DecimalType, a, self, 1); \ if (dec_a == NULL) return NULL; \ dec_b = (decimalobject *)_convert_to_decimal( \ &PyDecimal_DecimalType, b, self, 1); \ if (dec_b == NULL) { Py_DECREF(dec_a); return NULL; } \ res = (PyObject *)_do_decimal_##decname(dec_a, dec_b, self); \ Py_DECREF(dec_a); \ Py_DECREF(dec_b); \ return res; \ } /* helper so that we can use the CONTEXT_UNARY_FUNC macro above */ #define _do_decimal_abs_with_round(a, b) \ _do_decimal_absolute(a, b, 1) CONTEXT_UNARY_FUNC(abs, abs_with_round) CONTEXT_BINARY_FUNC(add, add) CONTEXT_BINARY_FUNC(divide, divide) CONTEXT_BINARY_FUNC(divide_int, floor_div) CONTEXT_BINARY_FUNC(divmod, divmod) CONTEXT_BINARY_FUNC(remainder, remainder) CONTEXT_UNARY_FUNC(minus, negative) CONTEXT_BINARY_FUNC(multiply, multiply) CONTEXT_UNARY_FUNC(plus, positive) CONTEXT_BINARY_FUNC(subtract, subtract) CONTEXT_BINARY_FUNC(compare, compare) CONTEXT_BINARY_FUNC(min, min) CONTEXT_BINARY_FUNC(max, max) CONTEXT_UNARY_FUNC(normalize, normalize) CONTEXT_UNARY_FUNC(trim, trim) CONTEXT_BINARY_FUNC(remainder_near, remainder_near) CONTEXT_UNARY_FUNC(sqrt, sqrt) CONTEXT_UNARY_FUNC(to_eng_string, to_eng_string) CONTEXT_UNARY_FUNC(exp, exp) CONTEXT_UNARY_FUNC(ln, ln) CONTEXT_UNARY_FUNC(log10, log10) CONTEXT_BINARY_FUNC(comparetotal, comparetotal) /* Unfortunately, the following methods are non-standard and can't be created by macros. */ static PyObject * context_apply(contextobject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"a", 0}; decimalobject *a; decimalobject *tmp; PyObject *ret; if(!PyArg_ParseTupleAndKeywords(args, kwds, "O:_apply", kwlist, &a)) return NULL; if(!PyDecimal_Check(a)) { PyErr_SetString(PyExc_ValueError, "a must be Decimal object"); return NULL; } tmp = _decimal_fix(a, self); if(!tmp) return NULL; ret = decimal_str(tmp); Py_DECREF(tmp); return ret; } static PyObject * context_power(contextobject *self, PyObject *args) { PyObject *a, *b, *c = NULL; decimalobject *dec_a = NULL, *dec_b = NULL, *dec_c = NULL, *res; if (!PyArg_ParseTuple(args, "OO|O:power", &a, &b, &c)) return NULL; dec_a = (decimalobject *)_convert_to_decimal( &PyDecimal_DecimalType, a, self, 1); if (dec_a == NULL) return NULL; dec_b = (decimalobject *)_convert_to_decimal( &PyDecimal_DecimalType, b, self, 1); if (dec_b == NULL) { Py_DECREF(dec_a); return NULL; } if (c) { dec_c = (decimalobject *)_convert_to_decimal( &PyDecimal_DecimalType, c, self, 1); if (dec_c == NULL) { dec_c = (decimalobject *)Py_None; Py_INCREF(Py_None); /* XXX is it ok ? */ PyErr_Clear(); } } else { dec_c = (decimalobject*)Py_None; Py_INCREF(Py_None); } res = _do_decimal_power(dec_a, dec_b, dec_c, self); Py_DECREF(dec_a); Py_DECREF(dec_b); Py_DECREF(dec_c); return (PyObject *)res; } static PyObject * context_quantize(contextobject *self, PyObject *args) { PyObject *a, *b; decimalobject *dec_a = NULL, *dec_b = NULL, *res; if (!PyArg_ParseTuple(args, "OO:quantize", &a, &b)) return NULL; dec_a = (decimalobject *)_convert_to_decimal( &PyDecimal_DecimalType, a, self, 1); if (dec_a == NULL) return NULL; dec_b = (decimalobject *)_convert_to_decimal( &PyDecimal_DecimalType, b, self, 1); if (dec_b == NULL) { Py_DECREF(dec_a); return NULL; } res = _do_decimal_quantize(dec_a, dec_b, self, -1, 1); Py_DECREF(dec_a); Py_DECREF(dec_b); return (PyObject *)res; } static PyObject * context_same_quantum(contextobject *self, PyObject *args) { PyObject *a, *b, *res; decimalobject *dec_a = NULL, *dec_b = NULL; if (!PyArg_ParseTuple(args, "OO:same_quantum", &a, &b)) return NULL; dec_a = (decimalobject *)_convert_to_decimal( &PyDecimal_DecimalType, a, self, 1); if (dec_a == NULL) return NULL; dec_b = (decimalobject *)_convert_to_decimal( &PyDecimal_DecimalType, b, self, 1); if (dec_b == NULL) { Py_DECREF(dec_a); return NULL; } res = _do_decimal_same_quantum(dec_a, dec_b); Py_DECREF(dec_a); Py_DECREF(dec_b); return res; } static PyObject * context_to_integral(contextobject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"a", 0}; PyObject *a, *res; decimalobject *dec_a = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:to_integral", kwlist, &a)) return NULL; dec_a = (decimalobject *)_convert_to_decimal( &PyDecimal_DecimalType, a, self, 1); if (dec_a == NULL) return NULL; res = (PyObject *)_do_decimal_to_integral(dec_a, self, -1); Py_DECREF(dec_a); return res; } static PyObject * context_to_sci_string(contextobject *self, PyObject *args, PyObject *kwds) { PyObject *a, *res; decimalobject *dec_a = NULL; static char *kwlist[] = {"a", 0}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:to_sci_string", kwlist, &a)) return NULL; dec_a = (decimalobject *)_convert_to_decimal( &PyDecimal_DecimalType, a, self, 1); if (dec_a == NULL) return NULL; res = _do_decimal_str(dec_a, self, 0); Py_DECREF(dec_a); return res; } static PyObject * context_reduce(contextobject *self) { PyObject *Emin, *Emax, *ret; Emin = exp_to_pyobj(self->Emin); if (!Emin) return NULL; Emax = exp_to_pyobj(self->Emax); if (!Emax) { Py_DECREF(Emin); return NULL; } /* Yes, it seems to be as simple as that. */ ret = Py_BuildValue("(O(liiOOOOiiO))", self->ob_type, self->prec, self->rounding, self->rounding_dec, self->traps, self->flags, Emin, Emax, self->capitals, self->clamp, self->ignored); Py_DECREF(Emin); Py_DECREF(Emax); return ret; } static PyObject * context_ignore_flags(contextobject *self, PyObject *args) { PyObject *flag, *ret_flags; long i, j; /* NB, unlike regard_flags this doesn't accept a sequence as the first element and regard_flags returns a list-ized version of its arguments instead of None */ ret_flags = PyList_New(0); if (ret_flags == NULL) return NULL; for (i = 0; i < PyTuple_GET_SIZE(args); i++) { flag = PyTuple_GET_ITEM(args, i); for (j = 0; j < NUMSIGNALS; j++) { if (errors[j] == flag) { _set_flag(self->ignored, j, 1); if (PyList_Append(ret_flags, flag) < 0) { Py_DECREF(ret_flags); return NULL; } break; } } /* No DECREF of flag here. */ if (j == NUMSIGNALS) { Py_DECREF(ret_flags); PyErr_SetString(PyExc_ValueError, "arguments must be valid flags"); return NULL; } } return ret_flags; } static PyObject * context_ignore_all_flags(contextobject *self) { PyObject *allflags, *flag, *res; int i = 0; allflags = PyTuple_New(NUMSIGNALS); if (allflags == NULL) return NULL; for (i = 0; i < NUMSIGNALS; i++) { flag = errors[i]; Py_INCREF(flag); PyTuple_SET_ITEM(allflags, i, flag); } res = context_ignore_flags(self, allflags); Py_DECREF(allflags); return res; } static PyObject * context_regard_flags(contextobject *self, PyObject *args) { PyObject *flag, *flags; long i, j; if (PyTuple_GET_SIZE(args) <= 0) { PyErr_BadInternalCall(); return NULL; } /* regard_flags allows a list of flags as the first arg */ flags = PySequence_GetItem(args, 0); /* if (PyTuple_GET_SIZE(args) != 1 && !PySequence_Check(flags))*/ /* flags = args; */ if (!PyTuple_Check(flags) && !PyList_Check(flags)) { Py_DECREF(flags); flags = args; } for (i = 0; i < PySequence_Size(flags); i++) { flag = PySequence_GetItem(flags, i); for (j = 0; j < NUMSIGNALS; j++) { if (flag == errors[j]) { _set_flag(self->ignored, j, 0); break; } } Py_DECREF(flag); if (j == NUMSIGNALS) { PyErr_SetString(PyExc_ValueError, "arguments must be valid flags"); return NULL; } } Py_RETURN_NONE; } static PyObject * context_set_rounding_decision(contextobject *self, PyObject *args) { int old_dec, new_dec; if (!PyArg_ParseTuple(args, "i:_set_rounding_decision", &new_dec)) return NULL; if (!VALID_ROUND_DEC(new_dec)) { PyErr_SetString(PyExc_ValueError, "value is not a valid rounding decision"); return NULL; } old_dec = self->rounding_dec; self->rounding_dec = new_dec; return PyInt_FromLong((long)old_dec); } static PyObject * context_set_rounding(contextobject *self, PyObject *args) { int old_round, new_round; if (!PyArg_ParseTuple(args, "i:_set_rounding", &new_round)) return NULL; if (!VALID_ROUND(new_round)) { PyErr_SetString(PyExc_ValueError, "value is not a valid rounding"); return NULL; } old_round = self->rounding; self->rounding = new_round; return PyInt_FromLong((long)old_round); } /* XXX: context_raise_error and friends should really vanish after there's no more Python code calling them. */ static PyObject * _do_context_error_dispatch(int flag, PyObject *args) { contextobject *ctx; decimalobject *thing; PyTypeObject *ctx_type; char *expl; long sign; int two; int ret; switch (flag) { case S_CLAMPED: case S_SUBNORMAL: case S_UNDERFLOW: case S_INEXACT: case S_ROUNDED: if (!PyArg_ParseTuple(args, "OO:_raise_error", &ctx, &expl)) return NULL; switch (flag) { case S_CLAMPED: ret = handle_Clamped(ctx, expl); break; case S_SUBNORMAL: ret = handle_Subnormal(ctx, expl); break; case S_UNDERFLOW: ret = handle_Underflow(ctx, expl); break; case S_INEXACT: ret = handle_Inexact(ctx, expl); break; case S_ROUNDED: ret = handle_Rounded(ctx, expl); break; }; break; case C_INV_CONTEXT: case C_DIV_IMPOSSIBLE: case C_CONV_SYNTAX: if (!PyArg_ParseTuple(args, "OOs:_raise_error", &ctx_type, &ctx, &expl)) return NULL; switch (flag) { case C_INV_CONTEXT: return (PyObject *)handle_InvalidContext(ctx_type, ctx, expl); break; case C_DIV_IMPOSSIBLE: return (PyObject *)handle_DivisionImpossible(ctx_type, ctx, expl); break; case C_CONV_SYNTAX: return (PyObject *)handle_ConversionSyntax(ctx_type, ctx, expl); break; }; break; case S_INV_OPERATION: if (!PyArg_ParseTuple(args, "OOsO:_raise_error", &ctx_type, &ctx, &expl, &thing)) return NULL; return (PyObject *)handle_InvalidOperation(ctx_type, ctx, expl, thing); break; case S_DIV_BY_ZERO: if (!PyArg_ParseTuple(args, "OOsli:_raise_error", &ctx_type, &ctx, &expl, &sign, &two)) return NULL; return (PyObject *)handle_DivisionByZero(ctx_type, ctx, expl, sign, two); break; case C_DIV_UNDEFINED: if (!PyArg_ParseTuple(args, "OOsl:_raise_error", &ctx_type, &ctx, &expl, &two)) return NULL; return (PyObject *)handle_DivisionUndefined(ctx_type, ctx, expl, two); break; case S_OVERFLOW: if (!PyArg_ParseTuple(args, "OOsl:_raise_error", &ctx_type, &ctx, &expl, &sign)) return NULL; return (PyObject *)handle_Overflow(ctx_type, ctx, expl, sign); break; default: printf("flag %d\n", flag); PyErr_SetString(PyExc_ValueError, "bad handler in _do_context_error_dispatch"); return NULL; } return PyLong_FromLong((long)ret); } static PyObject * context_raise_error(contextobject *self, PyObject *args, PyObject *kwds) { /* static char *kwlist[] = {"explanation", 0}; */ PyObject *condition = NULL; PyObject *explanation = NULL; PyObject *rest = NULL; PyObject *handler; PyObject *dummy; int errindex = -1, condindex = -1; int i; if (!PyArg_ParseTuple(args, "O|OO:_raise_error", &condition, &explanation, &rest)) return NULL; dummy = PyTuple_New(0); if (dummy == NULL) return NULL; /* if (explanation == NULL && !PyArg_ParseTupleAndKeywords(dummy, kwds, "O:_raise_error", kwlist, &explanation)) return NULL; */ if ((condition == ConversionSyntax) || (condition == DivisionImpossible) || (condition == DivisionUndefined) || (condition == InvalidContext)) { handler = InvalidOperation; } else { /* reuse the condition */ handler = condition; } /* The condition handle is called based on condition, the exception on handler. */ for (i = 0; i < NUMSIGNALS+NUMCONDITIONS; i++) { if (errors[i] == condition) condindex = i; if (errors[i] == handler) errindex = i; } if (_is_flag_set(self->ignored, errindex)) { return _do_context_error_dispatch(condindex, args); } else { _set_flag(self->flags, errindex, 1); if (!_is_flag_set(self->traps, errindex)) return _do_context_error_dispatch(condindex, args); } PyErr_SetString(handler, PyString_AsString(explanation)); return NULL; } /* XXX: no docstrings here, yet. The doctests of the original Python Context methods must be transferred to some other test module, because keeping them in here is a mess. There might actually be a doctest function that does this. */ /* 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, METH_NOARGS}, {"create_decimal", (PyCFunction)context_create_decimal, METH_VARARGS | METH_KEYWORDS}, {"Etiny", (PyCFunction)context_Etiny, METH_NOARGS}, {"Etop", (PyCFunction)context_Etop, METH_NOARGS}, {"abs", (PyCFunction)context_abs, METH_O}, {"add", (PyCFunction)context_add, METH_VARARGS}, {"compare", (PyCFunction)context_compare, METH_VARARGS}, {"divide", (PyCFunction)context_divide, METH_VARARGS}, {"divide_int", (PyCFunction)context_divide_int, METH_VARARGS}, {"divmod", (PyCFunction)context_divmod, METH_VARARGS}, {"max", (PyCFunction)context_max, METH_VARARGS}, {"min", (PyCFunction)context_min, METH_VARARGS}, {"minus", (PyCFunction)context_minus, METH_O}, {"multiply", (PyCFunction)context_multiply, METH_VARARGS}, {"normalize", (PyCFunction)context_normalize, METH_O}, {"trim", (PyCFunction)context_trim, METH_O}, {"plus", (PyCFunction)context_plus, METH_O}, {"power", (PyCFunction)context_power, METH_VARARGS}, {"quantize", (PyCFunction)context_quantize, METH_VARARGS}, {"remainder", (PyCFunction)context_remainder, METH_VARARGS}, {"remainder_near", (PyCFunction)context_remainder_near, METH_VARARGS}, {"same_quantum", (PyCFunction)context_same_quantum, METH_VARARGS}, {"sqrt", (PyCFunction)context_sqrt, METH_O}, {"exp", (PyCFunction)context_exp, METH_O}, {"ln", (PyCFunction)context_ln, METH_O}, {"log10", (PyCFunction)context_log10, METH_O}, {"comparetotal", (PyCFunction)context_comparetotal, METH_VARARGS}, {"subtract", (PyCFunction)context_subtract, METH_VARARGS}, {"to_eng_string", (PyCFunction)context_to_eng_string, METH_O}, {"to_integral", (PyCFunction)context_to_integral, METH_VARARGS | METH_KEYWORDS}, {"to_sci_string", (PyCFunction)context_to_sci_string, METH_VARARGS | METH_KEYWORDS}, {"_ignore_all_flags", (PyCFunction)context_ignore_all_flags, METH_NOARGS}, {"_ignore_flags", (PyCFunction)context_ignore_flags, METH_VARARGS}, {"_regard_flags", (PyCFunction)context_regard_flags, METH_VARARGS}, {"_set_rounding_decision", (PyCFunction)context_set_rounding_decision, METH_VARARGS}, {"_set_rounding", (PyCFunction)context_set_rounding, METH_VARARGS}, {"_raise_error", (PyCFunction)context_raise_error, METH_VARARGS | METH_KEYWORDS}, {"__copy__", (PyCFunction)context_copy, METH_NOARGS}, {"_shallow_copy", (PyCFunction)context_shallow_copy, METH_NOARGS}, {"__reduce__", (PyCFunction)context_reduce, METH_NOARGS}, {NULL, NULL} }; static char context_doc[] = PyDoc_STR("Contains the context for a Decimal instance."); static void context_dealloc(contextobject *c) { Py_DECREF(c->flags); Py_DECREF(c->traps); Py_DECREF(c->ignored); c->ob_type->tp_free(c); } #define TEST_AND_CAT(num, name) \ if (_is_flag_set(self->flags, num)) { \ strcat(flags, name); \ strcat(flags, ", "); \ flaglen += strlen(name) + 2; \ } \ if (_is_flag_set(self->traps, num)) { \ strcat(traps, name); \ strcat(traps, ", "); \ traplen += strlen(name) + 2; \ } static PyObject * context_repr(contextobject *self) { char roundstr[20] = "ROUND_"; char flags[250] = "["; /* 250 is enough for 12 error names of max. 17 chars */ char traps[250] = "["; /* and commas inbetween. */ #ifdef BIG_EXP char Emin[LOG * EXP_LIMB_COUNT + 1]; char Emax[LOG * EXP_LIMB_COUNT + 1]; #else char Emin[20]; char Emax[20]; #endif long flaglen = 1, traplen = 1; switch (self->rounding) { case ROUND_UP: strcat(roundstr, "UP"); break; case ROUND_DOWN: strcat(roundstr, "DOWN"); break; case ROUND_HALF_DOWN: strcat(roundstr, "HALF_DOWN"); break; case ROUND_HALF_EVEN: strcat(roundstr, "HALF_EVEN"); break; case ROUND_HALF_UP: strcat(roundstr, "HALF_UP"); break; case ROUND_FLOOR: strcat(roundstr, "FLOOR"); break; case ROUND_CEILING: strcat(roundstr, "CEILING"); break; case ROUND_05UP: strcat(roundstr, "05UP"); break; default: strcpy(roundstr, "None"); } TEST_AND_CAT(S_CLAMPED, "Clamped"); TEST_AND_CAT(S_INV_OPERATION, "InvalidOperation"); TEST_AND_CAT(S_DIV_BY_ZERO, "DivisionByZero"); TEST_AND_CAT(S_INEXACT, "Inexact"); TEST_AND_CAT(S_ROUNDED, "Rounded"); TEST_AND_CAT(S_SUBNORMAL, "Subnormal"); TEST_AND_CAT(S_OVERFLOW, "Overflow"); TEST_AND_CAT(S_UNDERFLOW, "Underflow"); /* if no flag/trap */ if (flaglen == 1) flaglen = 3; if (traplen == 1) traplen = 3; flags[flaglen-2] = ']'; flags[flaglen-1] = 0; traps[traplen-2] = ']'; traps[traplen-1] = 0; exp_sprintf(Emax, self->Emax); exp_sprintf(Emin, self->Emin); return PyString_FromFormat("Context(prec=%ld, rounding=%s, Emin=%s, " "Emax=%s, capitals=%d, flags=%s, traps=%s)", self->prec, roundstr, Emin, Emax, self->capitals, flags, traps); } #undef TEST_AND_CAT static long context_hash(PyObject *c) { PyErr_SetString(PyExc_TypeError, "Cannot hash a Context."); return -1; } /* TODO take a closer look at refs when error */ static PyObject * context_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"prec", "rounding", "_rounding_decision", "traps", "flags", "Emin", "Emax", "capitals", "_clamp", "_ignored_flags", 0}; long prec = LONG_MIN; PyObject *TEmin = NULL, *TEmax = NULL; exp_t Emin, Emax; int rounding = -1, rounding_dec = -1, capitals = -1, clamp = 0; PyObject *pytraps = NULL, *pyflags = NULL, *pyignored = NULL; PyObject *tmp, *res = NULL; PyObject *_traps = NULL; PyObject *_flags = NULL; PyObject *_ignored = NULL; int i, j; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|liiOOOOiiO:Context", kwlist, &prec, &rounding, &rounding_dec, &pytraps, &pyflags, &TEmin, &TEmax, &capitals, &clamp, &pyignored)) return NULL; if (!TEmin) Emin = exp_from_i(LONG_MAX); else { Emin = exp_from_pyobj(TEmin); if (PyErr_Occurred()) { goto err; } } if (!TEmax) Emax = exp_from_i(LONG_MIN); else { Emax = exp_from_pyobj(TEmax); if (PyErr_Occurred()) { goto err; } } if (pytraps == NULL) { _traps = PyDict_Copy(PyDecimal_DefaultContext->traps); if (!_traps) goto err; } else { _traps = PyDict_New(); if (!_traps) goto err; if (!PyDict_Check(pytraps)) { for (i = 0; i < NUMSIGNALS; i++) { j = PySequence_Contains(pytraps, errors[i]); if (j == -1) goto err; else if (j == 1) _set_flag(_traps, i, 1); else _set_flag(_traps, i, 0); } } else { for (i = 0; i < NUMSIGNALS; i++) { j = PyDict_Contains(pytraps, errors[i]); if (j == -1) goto err; else if (j == 0) continue; tmp = PyDict_GetItem(pytraps, errors[i]); if (!tmp) goto err; j = PyObject_IsTrue(tmp); if (j == -1) goto err; else if (j == 1) _set_flag(_traps, i, 1); else _set_flag(_traps, i, 0); } } } _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)) { for (i = 0; i < NUMSIGNALS; i++) { j = PyDict_Contains(pyflags, errors[i]); if (j == -1) goto err; else if (j == 0) continue; tmp = PyDict_GetItem(pyflags, errors[i]); if (!tmp) goto err; j = PyObject_IsTrue(tmp); if (j == -1) goto err; else if (j == 1) _set_flag(_flags, i, 1); } } else { PyErr_SetString(PyExc_TypeError, "initial flags must be a dict"); goto err; } _ignored = PyDict_New(); if (!_ignored) goto err; if (pyignored == NULL) { /* don't copy ignored flags from default context */ } else if (!PyDict_Check(pyignored)) { for (i = 0; i < NUMSIGNALS; i++) { j = PySequence_Contains(pyignored, errors[i]); if (j == -1) goto err; else if (j == 1) _set_flag(_ignored, i, 1); } } else { for (i = 0; i < NUMSIGNALS; i++) { j = PyDict_Contains(pyignored, errors[i]); if (j == -1) goto err; else if (j == 0) continue; tmp = PyDict_GetItem(pyignored, errors[i]); if (!tmp) goto err; j = PyObject_IsTrue(tmp); if (j == -1) goto err; else if (j == 1) _set_flag(_ignored, i, 1); } } if (prec == LONG_MIN) prec = PyDecimal_DefaultContext->prec; if (exp_eq_i(Emin, LONG_MAX)) Emin = PyDecimal_DefaultContext->Emin; if (exp_eq_i(Emax, LONG_MIN)) Emax = PyDecimal_DefaultContext->Emax; if (capitals == -1) capitals = PyDecimal_DefaultContext->capitals; else capitals = capitals & 1; /* if (rounding_dec == -1) */ if(rounding_dec != NEVER_ROUND && rounding_dec != ALWAYS_ROUND) /* XXX????*/ rounding_dec = PyDecimal_DefaultContext->rounding_dec; if (rounding_dec != NEVER_ROUND && rounding_dec != ALWAYS_ROUND) { PyErr_SetString(PyExc_ValueError, "_rounding_decision must be one of " "NEVER_ROUND or ALWAYS_ROUND"); return NULL; } if (rounding == -1) rounding = PyDecimal_DefaultContext->rounding; clamp = clamp & 1; res = (PyObject *)_new_contextobj(type, prec, rounding, rounding_dec, _traps, _flags, Emin, Emax, capitals, clamp, _ignored, 0); err: Py_XDECREF(_flags); Py_XDECREF(_traps); Py_XDECREF(_ignored); return res; } #define OFF(x) offsetof(contextobject, x) static PyMemberDef context_members[] = { {"prec", T_LONG, OFF(prec), 0}, {"capitals", T_INT, OFF(capitals), 0}, /* {"rounding", T_INT, OFF(rounding), 0}, */ /* {"_rounding_decision", T_INT, OFF(rounding_dec), 0}, */ {"_clamp", T_INT, OFF(clamp), 0}, {"flags", T_OBJECT_EX, OFF(flags), 0}, {"traps", T_OBJECT_EX, OFF(traps), 0}, {"_ignored_flags", T_OBJECT_EX, OFF(ignored), 0}, {NULL} }; static PyObject * context_get_rounding(contextobject *self) { return PyInt_FromLong(self->rounding); } static int context_set_rounding_(contextobject *self, PyObject *value) { int new_round = -1; if (PyInt_Check(value)) { new_round = PyInt_AsLong(value); if (PyErr_Occurred()) return -1; } if (PyString_Check(value)) { char *buffer = PyString_AS_STRING(value); if (!strcmp("ROUND_DOWN", buffer)) new_round = ROUND_DOWN; else if (!strcmp("ROUND_UP", buffer)) new_round = ROUND_UP; else if (!strcmp("ROUND_HALF_DOWN", buffer)) new_round = ROUND_HALF_DOWN; else if (!strcmp("ROUND_HALF_EVEN", buffer)) new_round = ROUND_HALF_EVEN; else if (!strcmp("ROUND_HALF_UP", buffer)) new_round = ROUND_HALF_UP; else if (!strcmp("ROUND_FLOOR", buffer)) new_round = ROUND_FLOOR; else if (!strcmp("ROUND_CEILING", buffer)) 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"); return -1; } if (!VALID_ROUND(new_round)) { PyErr_SetString(PyExc_ValueError, "value is not a valid rounding"); return -1; } self->rounding = new_round; return 0; } static PyObject * context_get_rounding_decision(contextobject *self) { return PyInt_FromLong(self->rounding_dec); } static int context_set_rounding_decision_(contextobject *self, PyObject *value) { int new_round_dec = -1; if (PyInt_Check(value)) { new_round_dec = PyInt_AsLong(value); if (PyErr_Occurred()) return -1; } if (PyString_Check(value)) { char *buffer = PyString_AS_STRING(value); if (!strcmp("ALWAYS_ROUND", buffer)) new_round_dec = 0; else if (!strcmp("NEVER_ROUND", buffer)) new_round_dec = 16; } else if (new_round_dec == -1) { PyErr_SetString(PyExc_TypeError, "Rounding should be int or string"); return -1; } if (!VALID_ROUND_DEC(new_round_dec)) { PyErr_SetString(PyExc_ValueError, "value is not a valid rounding decision"); return -1; } self->rounding_dec = new_round_dec; return 0; } static PyObject * context_get_emax(contextobject *self) { return exp_to_pyobj(self->Emax); } static int context_set_emax(contextobject *self, PyObject *value) { exp_t tmp = exp_from_pyobj(value); if (PyErr_Occurred()) return -1; self->Emax = tmp; return 0; } static PyObject * context_get_emin(contextobject *self) { return exp_to_pyobj(self->Emin); } static int context_set_emin(contextobject *self, PyObject *value) { exp_t tmp = exp_from_pyobj(value); if (PyErr_Occurred()) return -1; self->Emin = tmp; return 0; } static PyGetSetDef context_getset[] = { {"_rounding_decision", (getter)context_get_rounding_decision, (setter)context_set_rounding_decision_}, {"rounding", (getter) context_get_rounding, (setter)context_set_rounding_}, {"Emax", (getter) context_get_emax, (setter)context_set_emax}, {"Emin", (getter) context_get_emin, (setter)context_set_emin}, {NULL} }; /* XXX: If the dicts stay, Context might need to become a GC type */ static PyTypeObject PyDecimal_DecimalContextType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ MODULE_NAME ".Context", /* tp_name */ sizeof(contextobject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)context_dealloc,/* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)context_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ context_hash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_BASETYPE, /* tp_flags */ context_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ context_methods, /* tp_methods */ context_members, /* tp_members */ context_getset, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ context_new, /* tp_new */ PyObject_Del, /* tp_free */ }; /* module methods and initialization *****************************************/ static PyObject * module_getcontext(PyObject *self) { PyObject *ctx = (PyObject *)getcontext(); /* getcontext() returned a borrowed reference */ Py_XINCREF(ctx); return ctx; } static PyObject * module_setcontext(PyObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"context", 0}; contextobject *ctx; int res; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:setcontext", kwlist, &ctx)) return NULL; if (!PyDecimalContext_Check(ctx)) { PyErr_SetString(PyExc_TypeError, "setcontext() requires a Context object"); return NULL; } if (ctx == PyDecimal_BasicContext || ctx == PyDecimal_ExtendedContext || ctx == PyDecimal_DefaultContext) { ctx = (contextobject *)context_copy(ctx); res = setcontext(ctx); Py_DECREF(ctx); if (res == -1) return NULL; else Py_RETURN_NONE; } res = setcontext(ctx); if (res == -1) return NULL; else Py_RETURN_NONE; } static PyMethodDef module_methods[] = { {"getcontext", (PyCFunction)module_getcontext, METH_NOARGS}, {"setcontext", (PyCFunction)module_setcontext, METH_VARARGS | METH_KEYWORDS}, {NULL, NULL} }; #define INIT_EXC(m, name, base) \ name = PyErr_NewException(MODULE_NAME "." #name, base, NULL); \ if (!name) return; \ Py_INCREF(name); \ if (PyModule_AddObject(m, #name, name) < 0) { return; } #define INIT_EXC_WITH_2TUPLE(m, name, base1, base2) \ tup = PyTuple_Pack(2, base1, base2); \ if (!tup) return; \ INIT_EXC(m, name, tup); \ Py_DECREF(tup) #define INIT_EXC_WITH_3TUPLE(m, name, base1, base2, base3) \ tup = PyTuple_Pack(3, base1, base2, base3); \ if (!tup) return; \ INIT_EXC(m, name, tup) \ Py_DECREF(tup) #define ADD_CONST(m, name) \ if (PyModule_AddIntConstant(m, #name, name) < 0) return PyMODINIT_FUNC INITFUNC_NAME(void) { PyObject *m; /* a module object */ PyObject *tup; contextobject *ctx; PyObject *traps; traps = PyDict_New(); if (!traps) return; m = Py_InitModule3(MODULE_NAME, module_methods, "Fast implementation of decimal arithmetic."); if (m == NULL) return; if (PyType_Ready(&PyDecimal_DecimalType) < 0) return; if (PyType_Ready(&PyDecimal_DecimalContextType) < 0) return; Py_INCREF(&PyDecimal_DecimalType); PyModule_AddObject(m, "Decimal", (PyObject *) &PyDecimal_DecimalType); Py_INCREF(&PyDecimal_DecimalContextType); PyModule_AddObject(m, "Context", (PyObject *) &PyDecimal_DecimalContextType); ADD_CONST(m, ROUND_DOWN); ADD_CONST(m, ROUND_UP); ADD_CONST(m, ROUND_HALF_DOWN); ADD_CONST(m, ROUND_HALF_EVEN); 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); INIT_EXC(m, DecimalException, PyExc_ArithmeticError); INIT_EXC(m, Clamped, DecimalException); INIT_EXC(m, InvalidOperation, DecimalException); INIT_EXC(m, ConversionSyntax, InvalidOperation); INIT_EXC(m, DivisionImpossible, InvalidOperation); INIT_EXC_WITH_2TUPLE(m, DivisionUndefined, InvalidOperation, PyExc_ZeroDivisionError); INIT_EXC(m, InvalidContext, InvalidOperation); INIT_EXC_WITH_2TUPLE(m, DivisionByZero, DecimalException, PyExc_ZeroDivisionError); INIT_EXC(m, Inexact, DecimalException); INIT_EXC(m, Rounded, DecimalException); INIT_EXC(m, Subnormal, DecimalException); INIT_EXC_WITH_2TUPLE(m, Overflow, Rounded, Inexact); INIT_EXC_WITH_3TUPLE(m, Underflow, Rounded, Inexact, Subnormal); errors[S_INV_OPERATION] = InvalidOperation; errors[S_DIV_BY_ZERO] = DivisionByZero; errors[S_CLAMPED] = Clamped; errors[S_INEXACT] = Inexact; errors[S_ROUNDED] = Rounded; errors[S_SUBNORMAL] = Subnormal; errors[S_OVERFLOW] = Overflow; errors[S_UNDERFLOW] = Underflow; errors[C_INV_CONTEXT] = InvalidContext; errors[C_CONV_SYNTAX] = ConversionSyntax; errors[C_DIV_IMPOSSIBLE]= DivisionImpossible; errors[C_DIV_UNDEFINED] = DivisionUndefined; /* initialize default Context objects */ _set_flag(traps, S_DIV_BY_ZERO, 1); _set_flag(traps, S_OVERFLOW, 1); _set_flag(traps, S_INV_OPERATION, 1); PyDecimal_DefaultContext = _new_contextobj(&PyDecimal_DecimalContextType, 28, ROUND_HALF_EVEN, ALWAYS_ROUND, traps, NULL, exp_from_i(-999999999L), exp_from_i(999999999L), 1, 0, NULL, 1); if (!PyDecimal_DefaultContext) return; if (PyModule_AddObject(m, "DefaultContext", (PyObject *)PyDecimal_DefaultContext) < 0) return; /* add flags for BasicContext */ _set_flag(traps, S_CLAMPED, 1); _set_flag(traps, S_UNDERFLOW, 1); PyDecimal_BasicContext = _new_contextobj(&PyDecimal_DecimalContextType, 9, ROUND_HALF_UP, ALWAYS_ROUND, traps, NULL, exp_from_i(-999999999L), exp_from_i(999999999L), 1, 0, NULL, 1); if (!PyDecimal_BasicContext) return; if (PyModule_AddObject(m, "BasicContext", (PyObject *)PyDecimal_BasicContext) < 0) return; PyDecimal_ExtendedContext = _new_contextobj(&PyDecimal_DecimalContextType, 9, ROUND_HALF_EVEN, ALWAYS_ROUND, NULL, NULL, exp_from_i(-999999999L), exp_from_i(999999999L), 1, 0, NULL, 1); if (!PyDecimal_ExtendedContext) return; if (PyModule_AddObject(m, "ExtendedContext", (PyObject *)PyDecimal_ExtendedContext) < 0) return; ctx = getcontext(); if (!ctx) return; PyDecimal_NaN = (decimalobject *)PyDecimal_FromString("nan", 3, ctx); if (!PyDecimal_NaN) return; if (PyModule_AddObject(m, "NaN", (PyObject *)PyDecimal_NaN) < 0) return; PyDecimal_Inf = (decimalobject *)PyDecimal_FromString("inf", 3, ctx); if (!PyDecimal_Inf) return; if (PyModule_AddObject(m, "Inf", (PyObject *)PyDecimal_Inf) < 0) return; PyDecimal_NegInf = (decimalobject *)PyDecimal_FromString("-inf", 4, ctx); if (!PyDecimal_NegInf) return; if (PyModule_AddObject(m, "negInf", (PyObject *)PyDecimal_NegInf) < 0) return; } /* Yay! */

RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4