From: <otm...@us...> - 2011-03-30 15:43:41
|
Revision: 7278 http://jython.svn.sourceforge.net/jython/?rev=7278&view=rev Author: otmarhumbel Date: 2011-03-30 15:43:34 +0000 (Wed, 30 Mar 2011) Log Message: ----------- upgrade most of the math module to 2.6 Modified Paths: -------------- trunk/jython/Lib/test/test_math_jy.py trunk/jython/src/org/python/compiler/Module.java trunk/jython/src/org/python/modules/math.java Added Paths: ----------- trunk/jython/tests/java/org/python/compiler/ trunk/jython/tests/java/org/python/compiler/ModuleTest.java trunk/jython/tests/java/org/python/core/PyFloatTest.java Modified: trunk/jython/Lib/test/test_math_jy.py =================================================================== --- trunk/jython/Lib/test/test_math_jy.py 2011-03-30 15:17:27 UTC (rev 7277) +++ trunk/jython/Lib/test/test_math_jy.py 2011-03-30 15:43:34 UTC (rev 7278) @@ -7,6 +7,7 @@ from test import test_support inf = float('inf') +ninf = float('-inf') nan = float('nan') class MathTestCase(unittest.TestCase): @@ -17,7 +18,17 @@ self.assertNotEqual(mantissa, mantissa) self.assertEqual(exponent, 0) + def test_fmod(self): + self.assertEqual(-1e-100, math.fmod(-1e-100, 1e100)) + def test_hypot(self): + self.assert_(math.isnan(math.hypot(nan, nan))) + self.assertEqual(inf, math.hypot(inf, 4)) + self.assertEqual(inf, math.hypot(4, inf)) + self.assertEqual(inf, math.hypot(ninf, 4)) + self.assertEqual(inf, math.hypot(4, ninf)) + + def test_main(): test_support.run_unittest(MathTestCase) Modified: trunk/jython/src/org/python/compiler/Module.java =================================================================== --- trunk/jython/src/org/python/compiler/Module.java 2011-03-30 15:17:27 UTC (rev 7277) +++ trunk/jython/src/org/python/compiler/Module.java 2011-03-30 15:43:34 UTC (rev 7278) @@ -72,7 +72,8 @@ } class PyFloatConstant extends Constant implements ClassConstants, Opcodes { - + private static final double ZERO = 0.0; + final double value; PyFloatConstant(double value) { @@ -98,10 +99,14 @@ @Override public boolean equals(Object o) { if (o instanceof PyFloatConstant) { - return ((PyFloatConstant) o).value == value; - } else { - return false; + double oVal = ((PyFloatConstant)o).value; + if (ZERO == value) { + // math.copysign() needs to distinguish signs of zeroes + return oVal == value && Double.toString(oVal).equals(Double.toString(value)); + } + return oVal == value; } + return false; } } Modified: trunk/jython/src/org/python/modules/math.java =================================================================== --- trunk/jython/src/org/python/modules/math.java 2011-03-30 15:17:27 UTC (rev 7277) +++ trunk/jython/src/org/python/modules/math.java 2011-03-30 15:43:34 UTC (rev 7278) @@ -1,6 +1,8 @@ // Copyright (c) Corporation for National Research Initiatives package org.python.modules; +import java.math.BigInteger; + import org.python.core.ClassDictInit; import org.python.core.Py; import org.python.core.PyFloat; @@ -9,46 +11,127 @@ import org.python.core.PyObject; import org.python.core.PyTuple; +// +// +++++ test the corner cases for every method again (e.g. test_math_jy.py) +// + public class math implements ClassDictInit { public static PyFloat pi = new PyFloat(Math.PI); public static PyFloat e = new PyFloat(Math.E); + + private static final double ZERO = 0.0; + private static final double MINUS_ZERO = -0.0; + private static final double HALF = 0.5; + private static final double ONE = 1.0; + private static final double MINUS_ONE = -1.0; + private static final double TWO = 2.0; + private static final double EIGHT = 8.0; + private static final double INF = Double.POSITIVE_INFINITY; + private static final double NINF = Double.NEGATIVE_INFINITY; + private static final double NAN = Double.NaN; + private static final BigInteger MAX_LONG_BIGINTEGER = new BigInteger(String.valueOf(Long.MAX_VALUE)); + private static final BigInteger MIN_LONG_BIGINTEGER = new BigInteger(String.valueOf(Long.MIN_VALUE)); - public static void classDictInit(PyObject dict) { - } - private static double check(double v) { - if (Double.isNaN(v)) - throw Py.ValueError("math domain error"); - if (Double.isInfinite(v)) - throw Py.OverflowError("math range error"); - return v; + public static void classDictInit(@SuppressWarnings("unused") PyObject dict) { } public static double acos(double v) { - return check(Math.acos(v)); + if (isinf(v)) { + throwMathDomainValueError(); + } + if (isnan(v)) { + return v; + } + return Math.acos(v); } + + public static double acosh(double v) { + if (isninf(v)) { + throwMathDomainValueError(); + } + if (v == ZERO || v == MINUS_ONE) { + throwMathDomainValueError(); + } + if (isnan(v) || isinf(v)) { + return v; + } + return log(v + sqrt(v * v - 1)); + } public static double asin(double v) { - return check(Math.asin(v)); + if (isinf(v)) { + throwMathDomainValueError(); + } + if (isnan(v)) { + return v; + } + return Math.asin(v); } + + public static double asinh(double v) { + if (isnan(v) || isinf(v)) { + return v; + } + return log(v + sqrt(v * v + 1)); + } public static double atan(double v) { - return check(Math.atan(v)); + if (isnan(v)) { + return v; + } + return Math.atan(v); } + + public static double atanh(double v) { + if (isnan(v)) { + return v; + } + if (isinf(v) || Math.abs(v) == ONE) { + throwMathDomainValueError(); + } + return log((1 + v) / (1 - v)) / 2; + } public static double atan2(double v, double w) { - return check(Math.atan2(v, w)); + return Math.atan2(v, w); } + public static double ceil(PyObject v) { + return ceil(v.asDouble()); + } + public static double ceil(double v) { - return check(Math.ceil(v)); + if (isnan(v) || isinf(v)) { + return v; + } + return Math.ceil(v); } public static double cos(double v) { - return check(Math.cos(v)); + if (isnan(v) || isinf(v)) { + return NAN; + } + return Math.cos(v); } + public static double cosh(double v) { + if (isinf(v)) { + return INF; + } + if (isnan(v)) { + return v; + } + return HALF * (Math.exp(v) + Math.exp(-v)); + } + public static double exp(double v) { + if (isninf(v)) { + return ZERO; + } + if (isnan(v) || isinf(v)) { + return v; + } return check(Math.exp(v)); } @@ -57,7 +140,10 @@ } public static double floor(double v) { - return check(Math.floor(v)); + if (isnan(v) || isinf(v)) { + return v; + } + return Math.floor(v); } public static double log(PyObject v) { @@ -77,37 +163,83 @@ return doubleValue; } - private static double calculateLongLog(PyLong v) { - int e[] = new int[1]; - double x = v.scaledDoubleValue(e); - if (x <= 0.0) throw Py.ValueError("math domain error"); - return log(x) + (e[0]*8.0)*log(2.0); - } - - private static double applyLoggedBase(double loggedValue, PyObject base) { - double loggedBase; - if (base instanceof PyLong) { - loggedBase = calculateLongLog((PyLong)base); - } else { - loggedBase = log(base.asDouble()); + public static double pow(double v, double w) { + if (w == ZERO) { + return ONE; } - return check(loggedValue / loggedBase); + if (v == ONE) { + return v; + } + if (isnan(v) || isnan(w)) { + return NAN; + } + if (v == ZERO) { + if (w == ZERO) { + return ONE; + } else if (w > ZERO || ispinf(w)) { + return ZERO; + } else { + throwMathDomainValueError(); + } + } + if (isninf(v)) { + if (isninf(w)) { + return ZERO; + } + if (isinf(w)) { + return INF; + } + if (w == ZERO) { + return ONE; + } + if (w > ZERO) { + if (isOdd(w)) { + return NINF; + } + return INF; + } + if (isOdd(w)) { + return MINUS_ZERO; + } + return ZERO; + } + if (isninf(w)) { + if (v < ZERO) { + if (v == MINUS_ONE) { + return ONE; + } + if (v < MINUS_ONE) { + return ZERO; + } + return INF; + } + } + if (ispinf(w)) { + if (v < ZERO) { + if (v == MINUS_ONE) { + return ONE; + } + if (v < MINUS_ONE) { + return INF; + } + return ZERO; + } + } + if (v < ZERO && !isIntegral(w)) { + throwMathDomainValueError(); + } + return Math.pow(v, w); } - private static double log(double v) { - return check(Math.log(v)); - } - - public static double pow(double v, double w) { - return check(Math.pow(v, w)); - } - public static double sin(PyObject v) { return sin(v.asDouble()); } public static double sin(double v) { - return check(Math.sin(v)); + if (isnan(v)) { + return v; + } + return Math.sin(v); } public static double sqrt(PyObject v) { @@ -115,37 +247,61 @@ } public static double sqrt(double v) { - return check(Math.sqrt(v)); + if (isnan(v)) { + return v; + } + if (ispinf(v)) { + return v; + } + if (isninf(v) || v == MINUS_ONE) { + throwMathDomainValueError(); + } + return Math.sqrt(v); } public static double tan(double v) { - return check(Math.tan(v)); + if (isnan(v) || isinf(v)) { + return NAN; + } + return Math.tan(v); } public static double log10(PyObject v) { if (v instanceof PyLong) { - int e[] = new int[1]; - double x = ((PyLong)v).scaledDoubleValue(e); - if (x <= 0.0) throw Py.ValueError("math domain error"); - return log10(x) + (e[0]*8.0)*log10(2.0); + int exp[] = new int[1]; + double x = ((PyLong)v).scaledDoubleValue(exp); + if (x <= ZERO) { + throwMathDomainValueError(); + } + return log10(x) + (exp[0] * EIGHT) * log10(TWO); } return log10(v.asDouble()); } - private static double log10(double v) { - return check(Math.log10(v)); - } - public static double sinh(double v) { - return check(0.5 * (Math.exp(v) - Math.exp(-v))); + if (isnan(v)) { + return v; + } + if (isinf(v)) { + return v; + } + return HALF * (Math.exp(v) - Math.exp(-v)); } - public static double cosh(double v) { - return check(0.5 * (Math.exp(v) + Math.exp(-v))); - } - public static double tanh(double v) { - return check(sinh(v) / cosh(v)); + if (isnan(v)) { + return v; + } + if (isinf(v)) { + if (isninf(v)) { + return MINUS_ONE; + } + return ONE; + } + if (v == MINUS_ZERO) { + return v; + } + return sinh(v) / cosh(v); } public static double fabs(double v) { @@ -153,11 +309,33 @@ } public static double fmod(double v, double w) { + if (isnan(v) || isnan(w)) { + return NAN; + } + if (isinf(w)) { + return v; + } + if (w == ZERO) { + throwMathDomainValueError(); + } + if (isinf(v) && w == ONE) { + throwMathDomainValueError(); + } return v % w; } public static PyTuple modf(double v) { - double w = v % 1.0; + if (isnan(v)) { + return new PyTuple(new PyFloat(v), new PyFloat(v)); + } + if (isinf(v)) { + double first = ZERO; + if (isninf(v)) { + first = MINUS_ZERO; + } + return new PyTuple(new PyFloat(first), new PyFloat(v)); + } + double w = v % ONE; v -= w; return new PyTuple(new PyFloat(w), new PyFloat(v)); } @@ -165,39 +343,228 @@ public static PyTuple frexp(double x) { int exponent = 0; - if (Double.isNaN(x) || Double.isInfinite(x) || x == 0.0) { + if (isnan(x) || isinf(x) || x == ZERO) { exponent = 0; } else { short sign = 1; - if (x < 0.0) { + if (x < ZERO) { x = -x; sign = -1; } + + for (; x < HALF; x *= TWO, exponent--); // needs an empty statement - for (; x < 0.5; x *= 2.0, exponent--); + for (; x >= ONE; x *= HALF, exponent++); // needs an empty statement - for (; x >= 1.0; x *= 0.5, exponent++); - x *= sign; } return new PyTuple(new PyFloat(x), new PyInteger(exponent)); } public static double ldexp(double v, PyObject wObj) { - int w = wObj.asInt(); - return check(v * Math.pow(2.0, w)); + if (ZERO == v) { + return v; // can be negative zero + } + if (isinf(v)) { + return v; + } + if (isnan(v)) { + return v; + } + long w = getLong(wObj); + if (w == Long.MIN_VALUE) { + if (v > ZERO) { + return ZERO; + } + return MINUS_ZERO; + } + return checkOverflow(v * Math.pow(TWO, w)); } public static double hypot(double v, double w) { - return check(Math.hypot(v, w)); + if (isinf(v) || isinf(w)) { + return INF; + } + if (isnan(v) || isnan(w)) { + return NAN; + } + return Math.hypot(v, w); } public static double radians(double v) { - return check(Math.toRadians(v)); + return check(Math.toRadians(v)); } public static double degrees(double v) { - return check(Math.toDegrees(v)); + return check(Math.toDegrees(v)); } + + public static boolean isnan(double v) { + return Double.isNaN(v); + } + + /** + * @param v + * + * @return <code>true</code> if v is positive or negative infinity + */ + public static boolean isinf(double v) { + return Double.isInfinite(v); + } + + public static double copysign(double v, double w) { + if (isnan(v)) { + return NAN; + } + if (signum(v) == signum(w)) { + return v; + } + return v *= MINUS_ONE; + } + + public static PyLong factorial(double v) { + if (v == ZERO || v == ONE) { + return new PyLong(1); + } + if (v < ZERO || isnan(v) || isinf(v)) { + throwMathDomainValueError(); + } + if (!isIntegral(v)) { + throwMathDomainValueError(); + } + // long input should be big enough :-) + long value = (long)v; + BigInteger bi = new BigInteger(Long.toString(value)); + for (long l = value - 1; l > 1; l--) { + bi = bi.multiply(new BigInteger(Long.toString(l))); + } + return new PyLong(bi); + } + + public static double log1p(double v) { + return log(ONE + v); + } + + public static double fsum(double... values) { + // TODO need an iterable + // TODO need sys.float_info + return 0; + } + + private static double calculateLongLog(PyLong v) { + int exp[] = new int[1]; + double x = v.scaledDoubleValue(exp); + if (x <= ZERO) { + throwMathDomainValueError(); + } + return log(x) + (exp[0] * EIGHT) * log(TWO); + } + + private static double applyLoggedBase(double loggedValue, PyObject base) { + double loggedBase; + if (base instanceof PyLong) { + loggedBase = calculateLongLog((PyLong)base); + } else { + loggedBase = log(base.asDouble()); + } + return check(loggedValue / loggedBase); + } + + private static double log(double v) { + if (isninf(v)) { + throwMathDomainValueError(); + } + if (isinf(v) || isnan(v)) { + return v; + } + return Math.log(v); + } + + private static double log10(double v) { + if (isninf(v)) { + throwMathDomainValueError(); + } + if (isinf(v) || isnan(v)) { + return v; + } + return Math.log10(v); + } + + private static boolean isninf(double v) { + return v == NINF; + } + + private static boolean ispinf(double v) { + return v == INF; + } + + /** + * work around special Math.signum() behaviour for positive and negative zero + */ + private static double signum(double v) { + double signum = ONE; + if (v == ZERO) { + if ('-' == Double.toString(v).charAt(0)) { + signum = MINUS_ONE; + } + } else { + signum = Math.signum(v); + } + return signum; + } + + private static void throwMathDomainValueError() { + throw Py.ValueError("math domain error"); + } + + private static double check(double v) { + if (isnan(v)) + throwMathDomainValueError(); + if (isinf(v)) + throw Py.OverflowError("math range error"); + return v; + } + + private static double checkOverflow(double v) { + if (isinf(v)) { + throw Py.OverflowError("math range error"); + } + return v; + } + + /** + * convert a PyObject into a long between Long.MIN_VALUE and Long.MAX_VALUE + */ + private static long getLong(PyObject pyo) { + if (pyo instanceof PyLong) { + return getLong(((PyLong)pyo)); + } + return pyo.asLong(); + } + + /** + * convert a PyLong into a long between Long.MIN_VALUE and Long.MAX_VALUE + */ + private static long getLong(PyLong pyLong) { + BigInteger value = pyLong.getValue(); + if (value.compareTo(MAX_LONG_BIGINTEGER) > 0) { + return Long.MAX_VALUE; + } + if (value.compareTo(MIN_LONG_BIGINTEGER) < 0) { + return Long.MIN_VALUE; + } + return value.longValue(); + } + + + private static boolean isIntegral(double v) { + return ceil(v) - v == ZERO; + } + + + private static boolean isOdd(double v) { + return isIntegral(v) && v % TWO != ZERO; + } + } Added: trunk/jython/tests/java/org/python/compiler/ModuleTest.java =================================================================== --- trunk/jython/tests/java/org/python/compiler/ModuleTest.java (rev 0) +++ trunk/jython/tests/java/org/python/compiler/ModuleTest.java 2011-03-30 15:43:34 UTC (rev 7278) @@ -0,0 +1,21 @@ +package org.python.compiler; + +import junit.framework.TestCase; + +public class ModuleTest extends TestCase { + + /** + * In order to get testCopysign() in test_math.py passing, PyFloatConstant needs to distinguish + * between 0.0 and -0.0: + * + * <pre> + * # copysign should let us distinguish signs of zeros + * </pre> + */ + public void testPyFloatConstant_Zero() { + PyFloatConstant positiveZero = new PyFloatConstant(0.0); + PyFloatConstant negativeZero = new PyFloatConstant(-0.0); + assertNotSame(positiveZero, negativeZero); + assertFalse(positiveZero.equals(negativeZero)); + } +} Added: trunk/jython/tests/java/org/python/core/PyFloatTest.java =================================================================== --- trunk/jython/tests/java/org/python/core/PyFloatTest.java (rev 0) +++ trunk/jython/tests/java/org/python/core/PyFloatTest.java 2011-03-30 15:43:34 UTC (rev 7278) @@ -0,0 +1,63 @@ +package org.python.core; + +import junit.framework.TestCase; + +public class PyFloatTest extends TestCase { + + private final static double nan = Double.NaN; + + private final static double inf = Double.POSITIVE_INFINITY; + + private final static double ninf = Double.NEGATIVE_INFINITY; + + /** + * test the basic behavior of java.lang.Double extreme values + */ + public void test_Double_InfinityAndNaN() { + assertTrue(Double.NaN != nan); // this is the definition of NaN + assertTrue(Double.isNaN(nan)); + assertFalse(Double.isInfinite(nan)); + assertTrue(Double.POSITIVE_INFINITY == inf); + assertFalse(Double.isNaN(inf)); + assertTrue(Double.isInfinite(inf)); + assertTrue(Double.NEGATIVE_INFINITY == ninf); + assertFalse(Double.isNaN(ninf)); + assertTrue(Double.isInfinite(ninf)); + assertTrue(nan != inf); + assertTrue(nan != ninf); + assertTrue(inf != ninf); + } + + /** + * test extreme values + */ + public void testInfinityAndNaN() { + PyFloat fNan = new PyFloat(Double.NaN); + PyFloat fInf = new PyFloat(Double.POSITIVE_INFINITY); + PyFloat fNinf = new PyFloat(Double.NEGATIVE_INFINITY); + assertTrue(Double.NaN != fNan.getValue()); // this is the definition of NaN + assertTrue(Double.isNaN(fNan.getValue())); + assertFalse(Double.isInfinite(fNan.getValue())); + assertTrue(Double.POSITIVE_INFINITY == fInf.getValue()); + assertFalse(Double.isNaN(fInf.getValue())); + assertTrue(Double.isInfinite(fInf.getValue())); + assertTrue(Double.NEGATIVE_INFINITY == fNinf.getValue()); + assertFalse(Double.isNaN(fNinf.getValue())); + assertTrue(Double.isInfinite(fNinf.getValue())); + assertTrue(fNan.getValue() != fInf.getValue()); + assertTrue(fNan.getValue() != fNinf.getValue()); + assertTrue(fInf.getValue() != fNinf.getValue()); + } + + /** + * test formatting of extreme values + */ + public void testInfinityAndNaN_repr() { + PyFloat fNan = new PyFloat(Double.NaN); + PyFloat fInf = new PyFloat(Double.POSITIVE_INFINITY); + PyFloat fNinf = new PyFloat(Double.NEGATIVE_INFINITY); + assertEquals("nan", String.valueOf(fNan.__repr__())); + assertEquals("inf", String.valueOf(fInf.__repr__())); + assertEquals("-inf", String.valueOf(fNinf.__repr__())); + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |