[Python-checkins] r60824 - in python/branches/trunk-math: Lib/test/cmath.ctest Lib/test/test_cmath.py Lib/test/test_math.py Modules/cmathmodule.c

mark.dickinson python-checkins at python.org
Fri Feb 15 02:49:11 CET 2008


Author: mark.dickinson
Date: Fri Feb 15 02:49:11 2008
New Revision: 60824

Modified:
   python/branches/trunk-math/Lib/test/cmath.ctest
   python/branches/trunk-math/Lib/test/test_cmath.py
   python/branches/trunk-math/Lib/test/test_math.py
   python/branches/trunk-math/Modules/cmathmodule.c
Log:
Add C99-style special-value handling for cmath.rect, and allow a 
negative first argument to rect.


Modified: python/branches/trunk-math/Lib/test/cmath.ctest
==============================================================================
--- python/branches/trunk-math/Lib/test/cmath.ctest	(original)
+++ python/branches/trunk-math/Lib/test/cmath.ctest	Fri Feb 15 02:49:11 2008
@@ -1944,3 +1944,99 @@
 tan0022 tan 1.1615313900880577 1.7956298728647107 -> 0.041793186826390362 1.0375339546034792
 tan0023 tan 0.067014779477908945 5.8517361577457097 -> 2.2088639754800034e-06 0.9999836182420061
 
+------------------------------------------------------------------------
+-- rect: Conversion from polar coordinates to rectangular coordinates --
+------------------------------------------------------------------------
+--
+-- For cmath.rect, we can use the same testcase syntax as for the
+-- complex -> complex functions above, but here the input arguments
+-- should be interpreted as a pair of floating-point numbers rather
+-- than the real and imaginary parts of a complex number.
+--
+-- Here are the 'spirit of C99' rules for rect.  First, the short
+-- version:
+--
+--    rect(x, t) = exp(log(x)+it) for positive-signed x
+--    rect(x, t) = -exp(log(-x)+it) for negative-signed x
+--    rect(nan, t) = exp(nan + it), except that in rect(nan, +-0) the
+--      sign of the imaginary part is unspecified.
+--
+-- and now the long version:
+--
+--   rect(x, -t) = conj(rect(x, t)) for all x and t
+--   rect(-x, t) = -rect(x, t) for all x and t
+--   rect(+0, +0) returns +0 + i0
+--   rect(+0, inf) returns +- 0 +- i0, where the signs of the real and
+--     imaginary parts are unspecified.
+--   rect(x, inf) returns NaN + i NaN and raises the "invalid"
+--     floating-point exception, for finite nonzero x.
+--   rect(inf, inf) returns +-inf + i NaN and raises the "invalid"
+--     floating-point exception (where the sign of the real part of the
+--     result is unspecified).
+--   rect(inf, +0) returns inf+i0
+--   rect(inf, x) returns inf*cis(x), for finite nonzero x
+--   rect(inf, NaN) returns +-inf+i NaN, where the sign of the real part
+--     of the result is unspecified.
+--   rect(NaN, x) returns NaN + i NaN for all nonzero numbers (including
+--     infinities) x
+--   rect(NaN, 0) returns NaN +- i0, where the sign of the imaginary
+--     part is unspecified
+--   rect(NaN, NaN) returns NaN + i NaN
+--   rect(x, NaN) returns NaN + i NaN for finite nonzero x
+--   rect(+0, NaN) return +-0 +- i0, where the signs of the real and
+--     imaginary parts are unspecified.
+
+-- special values
+rect1000 rect 0.0 0.0 -> 0.0 0.0
+rect1001 rect 0.0 inf -> 0.0 0.0
+rect1002 rect 2.3 inf -> nan nan        invalid
+rect1003 rect inf inf -> inf nan        invalid
+rect1004 rect inf 0.0 -> inf 0.0
+rect1005 rect inf 1.4 -> inf inf
+rect1006 rect inf 2.8 -> -inf inf
+rect1007 rect inf 4.2 -> -inf -inf
+rect1008 rect inf 5.6 -> inf -inf
+rect1009 rect inf 7.0 -> inf inf
+rect1010 rect nan 0.0 -> nan 0.0
+rect1011 rect nan 2.3 -> nan nan
+rect1012 rect nan inf -> nan nan
+rect1013 rect nan nan -> nan nan
+rect1014 rect inf nan -> inf nan
+rect1015 rect 2.3 nan -> nan nan
+rect1016 rect 0.0 nan -> 0.0 0.0
+rect1017 rect 0.0 -0.0 -> 0.0 -0.0
+rect1018 rect 0.0 -inf -> 0.0 0.0
+rect1019 rect 2.3 -inf -> nan nan       invalid
+rect1020 rect inf -inf -> inf nan       invalid
+rect1021 rect inf -0.0 -> inf -0.0
+rect1022 rect inf -1.4 -> inf -inf
+rect1023 rect inf -2.8 -> -inf -inf
+rect1024 rect inf -4.2 -> -inf inf
+rect1025 rect inf -5.6 -> inf inf
+rect1026 rect inf -7.0 -> inf -inf
+rect1027 rect nan -0.0 -> nan 0.0
+rect1028 rect nan -2.3 -> nan nan
+rect1029 rect nan -inf -> nan nan
+rect1030 rect -0.0 0.0 -> -0.0 -0.0
+rect1031 rect -0.0 inf -> 0.0 0.0
+rect1032 rect -2.3 inf -> nan nan       invalid
+rect1033 rect -inf inf -> -inf nan      invalid
+rect1034 rect -inf 0.0 -> -inf -0.0
+rect1035 rect -inf 1.4 -> -inf -inf
+rect1036 rect -inf 2.8 -> inf -inf
+rect1037 rect -inf 4.2 -> inf inf
+rect1038 rect -inf 5.6 -> -inf inf
+rect1039 rect -inf 7.0 -> -inf -inf
+rect1040 rect -inf nan -> inf nan
+rect1041 rect -2.3 nan -> nan nan
+rect1042 rect -0.0 nan -> 0.0 0.0
+rect1043 rect -0.0 -0.0 -> -0.0 0.0
+rect1044 rect -0.0 -inf -> 0.0 0.0
+rect1045 rect -2.3 -inf -> nan nan      invalid
+rect1046 rect -inf -inf -> -inf nan     invalid
+rect1047 rect -inf -0.0 -> -inf 0.0
+rect1048 rect -inf -1.4 -> -inf inf
+rect1049 rect -inf -2.8 -> inf inf
+rect1050 rect -inf -4.2 -> inf -inf
+rect1051 rect -inf -5.6 -> -inf -inf
+rect1052 rect -inf -7.0 -> -inf inf

Modified: python/branches/trunk-math/Lib/test/test_cmath.py
==============================================================================
--- python/branches/trunk-math/Lib/test/test_cmath.py	(original)
+++ python/branches/trunk-math/Lib/test/test_cmath.py	Fri Feb 15 02:49:11 2008
@@ -268,10 +268,19 @@
     def test_specific_values(self):
         if not float.__getformat__("double").startswith("IEEE"):
             return
+
+        def rect_complex(z):
+            """Wrapped version of rect that accepts a complex number instead of
+            two float arguments."""
+            return cmath.rect(z.real, z.imag)
+
         for id, fn, ar, ai, er, ei, flags in parse_testfile(test_file):
             arg = complex(ar, ai)
             expected = complex(er, ei)
-            function = getattr(cmath, fn)
+            if fn == 'rect':
+                function = rect_complex
+            else:
+                function = getattr(cmath, fn)
             if 'divide-by-zero' in flags or 'invalid' in flags:
                 self.assertRaises(ValueError, function, arg)
                 continue

Modified: python/branches/trunk-math/Lib/test/test_math.py
==============================================================================
--- python/branches/trunk-math/Lib/test/test_math.py	(original)
+++ python/branches/trunk-math/Lib/test/test_math.py	Fri Feb 15 02:49:11 2008
@@ -554,6 +554,9 @@
             # flags is nonempty
             if ai != 0. or ei != 0. or flags:
                 continue
+            if fn == 'rect':
+                # no real analogue of rect
+                continue
             func = getattr(math, fn)
             result = func(ar)
             self.ftest("%s:%s(%r)" % (id, fn, ar), result, er)

Modified: python/branches/trunk-math/Modules/cmathmodule.c
==============================================================================
--- python/branches/trunk-math/Modules/cmathmodule.c	(original)
+++ python/branches/trunk-math/Modules/cmathmodule.c	Fri Feb 15 02:49:11 2008
@@ -993,6 +993,27 @@
 Convert a complex from rectangular coordinates to polar coordinates. r is\n\
 the distance from 0 and phi the phase angle.");
 
+/*
+  rect() isn't covered by the C99 standard, but it's not too hard to
+  figure out 'spirit of C99' rules for special value handing:
+
+    rect(x, t) should behave like exp(log(x), t) for positive-signed x
+    rect(x, t) should behave like -exp(log(-x), t) for negative-signed x
+    rect(nan, t) should behave like exp(nan, t), except that rect(nan, 0)
+      gives nan +- 0j with the sign of the imaginary part unspecified.
+
+*/
+
+static Py_complex rect_special_values[7][7] = {
+	{{INF,N},{U,U},{-INF,0.},{-INF,-0.},{U,U},{INF,N},{INF,N}},
+	{{N,N},  {U,U},{U,U},    {U,U},     {U,U},{N,N},  {N,N}},
+	{{0.,0.},{U,U},{-0.,0.}, {-0.,-0.}, {U,U},{0.,0.},{0.,0.}},
+	{{0.,0.},{U,U},{0.,-0.}, {0.,0.},   {U,U},{0.,0.},{0.,0.}},
+	{{N,N},  {U,U},{U,U},    {U,U},     {U,U},{N,N},  {N,N}},
+	{{INF,N},{U,U},{INF,-0.},{INF,0.},  {U,U},{INF,N},{INF,N}},
+	{{N,N},  {N,N},{N,0.},   {N,0.},    {N,N},{N,N},  {N,N}}
+};
+
 static PyObject *
 cmath_rect(PyObject *self, PyObject *args)
 {
@@ -1001,13 +1022,41 @@
 	if (!PyArg_ParseTuple(args, "dd:rect", &r, &phi))
 		return NULL;
 	errno = 0;
-	if (r < 0.) {
-		errno = EDOM;
-		return math_error();
-	}
 	PyFPE_START_PROTECT("rect function", return 0)
-	z.real = r * cos(phi);
-	z.imag = r * sin(phi);
+
+	/* deal with special values */
+	if (!Py_IS_FINITE(r) || !Py_IS_FINITE(phi)) {
+		/* if r is +/-infinity and phi is finite but nonzero then
+		   result is (+-INF +-INF i), but we need to compute cos(phi)
+		   and sin(phi) to figure out the signs. */
+		if (Py_IS_INFINITY(r) && (Py_IS_FINITE(phi)
+					  && (phi != 0.))) {
+			if (r > 0) {
+				z.real = copysign(INF, cos(phi));
+				z.imag = copysign(INF, sin(phi));
+			}
+			else {
+				z.real = -copysign(INF, cos(phi));
+				z.imag = -copysign(INF, sin(phi));
+			}
+		}
+		else {
+			z = rect_special_values[special_type(r)]
+				               [special_type(phi)];
+		}
+		/* need to set errno = EDOM if r is a nonzero number and phi
+		   is infinite */
+		if (r != 0. && !Py_IS_NAN(r) && Py_IS_INFINITY(phi))
+			errno = EDOM;
+		else
+			errno = 0;
+	}
+	else {
+		z.real = r * cos(phi);
+		z.imag = r * sin(phi);
+		errno = 0;
+	}
+
 	PyFPE_END_PROTECT(z)
 	if (errno != 0)
 		return math_error();


More information about the Python-checkins mailing list