summaryrefslogtreecommitdiff
path: root/tests/run/cpow.pyx
diff options
context:
space:
mode:
Diffstat (limited to 'tests/run/cpow.pyx')
-rw-r--r--tests/run/cpow.pyx264
1 files changed, 264 insertions, 0 deletions
diff --git a/tests/run/cpow.pyx b/tests/run/cpow.pyx
new file mode 100644
index 000000000..6f050337c
--- /dev/null
+++ b/tests/run/cpow.pyx
@@ -0,0 +1,264 @@
+# mode: run
+# tag: warnings
+
+from __future__ import print_function
+
+cimport cython
+import sys
+
+if sys.version_info[0] > 2:
+ # The <object> path doesn't work in Py2
+ __doc__ = """
+ >>> pow_double_double(-4, 0.5, 1e-15)
+ soft double complex complex
+ """
+
+def pow_double_double(double a, double b, delta):
+ """
+ >>> pow_double_double(2, 2, 1e-15)
+ soft double complex float
+ >>> pow_double_double(4, 0.5, 1e-15)
+ soft double complex float
+ """
+ c = a**b
+ # print out the Cython type, and the coerced type
+ print(cython.typeof(c), type(c).__name__)
+ object_c = (<object>a)**(<object>b)
+ assert abs((c/object_c) - 1) < delta
+
+@cython.cpow(True)
+def pow_double_double_cpow(double a, double b, delta=None):
+ """
+ >>> pow_double_double_cpow(2, 2, 1e-15)
+ double float
+ >>> pow_double_double_cpow(4, 0.5, 1e-15)
+ double float
+ >>> x = pow_double_double_cpow(-4, 0.5)
+ double float
+ >>> x == x # is nan
+ False
+ """
+ c = a**b
+ # print out the Cython type, and the coerced type
+ print(cython.typeof(c), type(c).__name__)
+ if delta is not None:
+ object_c = (<object>a)**(<object>b)
+ assert abs((c/object_c) - 1) < delta
+ else:
+ return c
+
+cdef cfunc_taking_double(double x):
+ return x
+
+def pow_double_double_coerced_directly(double a, double b):
+ """
+ >>> pow_double_double_coerced_directly(2, 2)
+ 8.0
+ >>> x = pow_double_double_coerced_directly(-2, 0.5)
+ >>> x == x # nan
+ False
+ """
+ # Because we're assigning directly to a double assume 'cpow'
+ # but warn.
+ cdef double c = a**b
+ return cfunc_taking_double(a**b) + c
+
+def pow_double_int(double a, int b):
+ """
+ # a few variations of 'double**int'. In all cases
+ # Cython should realise that the result can't be complex
+ # and avoid going through the soft complex type
+ >>> pow_double_int(5, 2)
+ double
+ double
+ double
+ double
+ double
+ """
+ c1 = a**b
+ c2 = a**2.0
+ c3 = a**-2.0
+ c4 = a**5
+ c5 = a**-5
+ print(cython.typeof(c1))
+ print(cython.typeof(c2))
+ print(cython.typeof(c3))
+ print(cython.typeof(c4))
+ print(cython.typeof(c5))
+
+def soft_complex_coerced_to_double(double a, double b):
+ """
+ >>> soft_complex_coerced_to_double(2, 2)
+ 4.0
+ >>> soft_complex_coerced_to_double(-2, 0.25)
+ Traceback (most recent call last):
+ ...
+ TypeError: Cannot convert 'complex' with non-zero imaginary component to 'double' (this most likely comes from the '**' operator; use 'cython.cpow(True)' to return 'nan' instead of a complex number).
+ """
+ c = a**b
+ assert cython.typeof(c) == "soft double complex"
+ cdef double d = c # will raise if complex
+ return d
+
+def soft_complex_coerced_to_complex(double a, double b):
+ """
+ >>> soft_complex_coerced_to_complex(2, 2)
+ (4+0j)
+ >>> x = soft_complex_coerced_to_complex(-1, 0.5)
+ >>> abs(x.real) < 1e-15
+ True
+ >>> abs(x.imag - 1) < 1e-15
+ True
+ """
+ # This is always fine, but just check it works
+ c = a**b
+ assert cython.typeof(c) == "soft double complex"
+ cdef double complex d = c
+ return d
+
+def soft_complex_type_inference_1(double a, double b, pick):
+ """
+ >>> soft_complex_type_inference_1(2, 1, False)
+ soft double complex 2.0
+ >>> soft_complex_type_inference_1(2, 3, True)
+ soft double complex 4.0
+ """
+ # double and soft complex should infer to soft-complex
+ if pick:
+ c = a**2
+ else:
+ c = a**b
+ print(cython.typeof(c), c)
+
+def soft_complex_type_inference_2(double a, double b, expected):
+ """
+ >>> soft_complex_type_inference_2(2, 1, 1.0)
+ soft double complex
+ >>> soft_complex_type_inference_2(2, 3, 7.0)
+ soft double complex
+ """
+ # double and soft complex should infer to soft-complex
+ c = a**b
+ c -= 1
+ print(cython.typeof(c))
+ delta = abs(c/expected - 1)
+ assert delta < 1e-15, delta
+
+def pow_int_int(int a, int b):
+ """
+ >>> pow_int_int(2, 2)
+ double 4.0
+ >>> pow_int_int(2, -2)
+ double 0.25
+ """
+ c = a**b
+ print(cython.typeof(c), c)
+
+@cython.cpow(True)
+def pow_int_int_cpow(int a, int b):
+ """
+ >>> pow_int_int_cpow(2, 2)
+ int 4
+ >>> pow_int_int_cpow(2, -2)
+ int 0
+ """
+ c = a**b
+ print(cython.typeof(c), c)
+
+cdef cfunc_taking_int(int x):
+ return x
+
+def pow_int_int_coerced_directly(int a, int b):
+ """
+ Generates two warnings about using cpow.
+ The actual behaviour isn't too easy to distinguish
+ without inspecting the c code though.
+ >>> pow_int_int_coerced_directly(2, 2)
+ 8
+ """
+ cdef int c = a**b
+ return cfunc_taking_int(a**b) + c
+
+def pow_int_int_non_negative(int a, unsigned int b):
+ """
+ A couple of combinations of non-negative values for the
+ exponent, which lets us fall back to int as a return type
+ >>> pow_int_int_non_negative(5, 3)
+ unsigned int
+ long
+ """
+ c1 = a**b
+ c2 = a**5
+ print(cython.typeof(c1))
+ print(cython.typeof(c2))
+
+
+ctypedef double f64
+
+def pythagoras_with_typedef(double a, double b):
+ # see https://github.com/cython/cython/issues/5203
+ """
+ >>> rc = pythagoras_with_typedef(2.0, 2.0)
+ >>> pyresult = 1.0 / (2 * 2.0 ** 2) ** 0.5
+ >>> pyresult - 0.001 < rc < pyresult + 0.001 or (rc, pyresult)
+ True
+ """
+ cdef f64 result = a * a + b * b
+ result = 1.0 / result ** 0.5
+ return result
+
+
+@cython.cpow(False)
+def power_coercion_in_nogil_1(double a, double b):
+ """
+ >>> power_coercion_in_nogil_1(2., 2.)
+ 4.0
+ >>> power_coercion_in_nogil_1(-1., 0.5)
+ Traceback (most recent call last):
+ ...
+ TypeError: Cannot convert 'complex' with non-zero imaginary component to 'double' (this most likely comes from the '**' operator; use 'cython.cpow(True)' to return 'nan' instead of a complex number).
+ """
+ cdef double c
+ with nogil:
+ c = a**b
+ return c
+
+
+cdef double nogil_fun(double x) nogil:
+ return x
+
+def power_coercion_in_nogil_2(double a, double b):
+ """
+ >>> power_coercion_in_nogil_2(2., 2.)
+ 4.0
+ >>> power_coercion_in_nogil_2(-1., 0.5)
+ Traceback (most recent call last):
+ ...
+ TypeError: Cannot convert 'complex' with non-zero imaginary component to 'double' (this most likely comes from the '**' operator; use 'cython.cpow(True)' to return 'nan' instead of a complex number).
+ """
+ c = a**b
+ with nogil:
+ d = nogil_fun(c)
+ return d
+
+
+def power_coercion_in_nogil_3(double a, double b, double c):
+ """
+ >>> power_coercion_in_nogil_3(2., 2., 1.0)
+ 0.25
+ >>> power_coercion_in_nogil_3(-1., 0.5, 1.0)
+ Traceback (most recent call last):
+ ...
+ TypeError: Cannot convert 'complex' with non-zero imaginary component to 'double' (this most likely comes from the '**' operator; use 'cython.cpow(True)' to return 'nan' instead of a complex number).
+ """
+ with nogil:
+ c /= a**b
+ return c
+
+
+_WARNINGS = """
+63:21: Treating '**' as if 'cython.cpow(True)' since it is directly assigned to a a non-complex C numeric type. This is likely to be fragile and we recommend setting 'cython.cpow' explicitly.
+64:32: Treating '**' as if 'cython.cpow(True)' since it is directly assigned to a a non-complex C numeric type. This is likely to be fragile and we recommend setting 'cython.cpow' explicitly.
+179:18: Treating '**' as if 'cython.cpow(True)' since it is directly assigned to a an integer C numeric type. This is likely to be fragile and we recommend setting 'cython.cpow' explicitly.
+180:29: Treating '**' as if 'cython.cpow(True)' since it is directly assigned to a an integer C numeric type. This is likely to be fragile and we recommend setting 'cython.cpow' explicitly.
+"""