diff -r 685b6198dcf5 src/org/python/modules/math.java --- a/src/org/python/modules/math.java Tue Apr 24 15:34:58 2012 -0700 +++ b/src/org/python/modules/math.java Tue Apr 24 15:38:01 2012 -0700 @@ -9,6 +9,7 @@ import org.python.core.PyInteger; import org.python.core.PyLong; import org.python.core.PyObject; +import org.python.core.PySystemState; import org.python.core.PyTuple; import org.python.modules.math_erf; @@ -507,12 +508,63 @@ return log(ONE + v); } - public static double fsum(double... values) { - // TODO need an iterable - // TODO need sys.float_info - return 0; + /* + * Full precision summation. Compute sum(iterable) without any + * intermediate accumulation of error. Based on the 'lsum' function at + * http://code.activestate.com/recipes/393090/ + * + * Translation of msum in test_math.py + */ + public static double fsum(final PyObject iterable) { + PyObject iter = Py.iter(iterable, "fsum() argument must support iteration"); + + long mant_dig = ((PyLong)(PySystemState.float_info.__getattr__("mant_dig"))).asLong(); + PyObject py_mant_dig = new PyLong(mant_dig); + long min_exp = ((PyLong)(PySystemState.float_info.__getattr__("min_exp"))).asLong(); + long etiny = min_exp - mant_dig; + + long tmant = 0; + long texp = 0; + + for (PyObject item; (item = iter.__iternext__()) != null;) { + if (!(item instanceof PyFloat)) { + throw Py.ValueError("non float argument to fsum()"); + } + PyTuple tup = frexp(((PyFloat)(item)).asDouble()); + double mant = ((PyFloat)(tup.get(0))).asDouble(); + long exp = ((PyInteger)(tup.get(1))).asLong(); + long imant = (int)ldexp(mant, py_mant_dig); + exp = exp - mant_dig; + if (texp > exp) { + tmant <<= texp-exp; + texp = exp; + } else { + imant <<= exp-texp; + tmant += imant; + } + } + + /* Round tmant * 2**texp to a float. The original recipe + used float(str(tmant)) * 2.0**texp for this, but that's + a little unsafe because str -> float conversion can't be + relied upon to do correct rounding on all platforms. + */ + long nbits = Long.toBinaryString(tmant).length(); + long tail = Math.max(nbits - mant_dig, etiny - texp); + if (tail > 0) { + long h = 1 << (tail-1); + /* XXX: This is ugly */ + boolean cond1 = ((tmant & h) == 0) ? false : true; + boolean cond2 = ((tmant & 3*h-1) == 0) ? false : true; + long i = (cond1 && cond2) ? 1 : 0; + tmant = Math.round(tmant / (2*h)) + i; + texp += tail; + } + + return math.ldexp(tmant, new PyFloat(texp)); } + private static double calculateLongLog(PyLong v) { int exp[] = new int[1]; double x = v.scaledDoubleValue(exp);