diff --git a/pyopencl/clmath.py b/pyopencl/clmath.py
new file mode 100644
index 0000000000000000000000000000000000000000..9500486c01003a6b420b2cdae8facf21bac5ca3d
--- /dev/null
+++ b/pyopencl/clmath.py
@@ -0,0 +1,151 @@
+import pyopencl.array as cl_array
+import pyopencl.elementwise as elementwise
+
+def _make_unary_array_func(name):
+    def f(array, queue=None):
+        result = array._new_like_me()
+
+        knl = elementwise.get_unary_func_kernel(array.context, name, array.dtype)
+        knl(queue or array.queue, array._global_size, array._local_size,
+                array.data, result.data, array.mem_size)
+
+        return result
+    return f
+
+# See table 6.8 in the CL spec
+acos = _make_unary_array_func("acos")
+acosh = _make_unary_array_func("acosh")
+acospi = _make_unary_array_func("acospi")
+
+asin = _make_unary_array_func("asin")
+asinh = _make_unary_array_func("asinh")
+asinpi = _make_unary_array_func("asinpi")
+
+atan = _make_unary_array_func("atan")
+# TODO: atan2
+atanh = _make_unary_array_func("atanh")
+atanpi = _make_unary_array_func("atanpi")
+# TODO: atan2pi
+
+cbrt = _make_unary_array_func("cbrt")
+ceil = _make_unary_array_func("ceil")
+# TODO: copysign
+
+cos = _make_unary_array_func("cos")
+cosh = _make_unary_array_func("cosh")
+cospi = _make_unary_array_func("cospi")
+
+erfc = _make_unary_array_func("erfc")
+erf = _make_unary_array_func("erf")
+exp = _make_unary_array_func("exp")
+exp2 = _make_unary_array_func("exp2")
+exp10 = _make_unary_array_func("exp10")
+expm1 = _make_unary_array_func("expm1")
+
+fabs = _make_unary_array_func("fabs")
+# TODO: fdim
+floor = _make_unary_array_func("floor")
+# TODO: fma
+# TODO: fmax
+# TODO: fmin
+
+def fmod(arg, mod, queue=None):
+    """Return the floating point remainder of the division `arg/mod`,
+    for each element in `arg` and `mod`."""
+    result = arg._new_like_me()
+
+    knl = elementwise.get_fmod_kernel(arg.context)
+    knl(queue or arg.queue, arg._global_size, arg._local_size,
+            arg.data, mod.data, result.data, arg.mem_size)
+
+    return result
+
+# TODO: fract
+
+def frexp(arg, queue=None):
+    """Return a tuple `(significands, exponents)` such that
+    `arg == significand * 2**exponent`.
+    """
+    sig = arg._new_like_me()
+    expt = arg._new_like_me()
+
+    knl = elementwise.get_frexp_kernel(arg.context)
+    knl(queue or arg.queue, arg._global_size, arg._local_size,
+            arg.data, sig.data, expt.data, arg.mem_size)
+
+    return sig, expt
+
+# TODO: hypot
+
+ilogb = _make_unary_array_func("ilogb")
+
+def ldexp(significand, exponent, queue=None):
+    """Return a new array of floating point values composed from the
+    entries of `significand` and `exponent`, paired together as
+    `result = significand * 2**exponent`.
+    """
+    result = significand._new_like_me()
+
+    knl = elementwise.get_ldexp_kernel(significand.context)
+    knl(queue or significand.queue,
+            significand._global_size, significand._local_size,
+            significand.data, exponent.data, result.data,
+            significand.mem_size)
+
+    return result
+
+lgamma = _make_unary_array_func("lgamma")
+# TODO: lgamma_r
+
+log = _make_unary_array_func("log")
+log2 = _make_unary_array_func("log2")
+log10 = _make_unary_array_func("log10")
+log1p = _make_unary_array_func("log1p")
+logb = _make_unary_array_func("logb")
+
+# TODO: mad
+# TODO: maxmag
+# TODO: minmag
+
+def modf(arg, queue=None):
+    """Return a tuple `(fracpart, intpart)` of arrays containing the
+    integer and fractional parts of `arg`.
+    """
+
+    intpart = arg._new_like_me()
+    fracpart = arg._new_like_me()
+
+    knl = elementwise.get_modf_kernel(arg.context)
+    knl(queue or arg.queue, arg._global_size, arg._local_size,
+            arg.data, intpart.data, fracpart.data,
+            arg.mem_size)
+
+    return fracpart, intpart
+
+nan = _make_unary_array_func("nan")
+
+# TODO: nextafter
+# TODO: remainder
+# TODO: remquo
+
+rint = _make_unary_array_func("rint")
+# TODO: rootn
+round = _make_unary_array_func("round")
+
+sin = _make_unary_array_func("sin")
+# TODO: sincos
+sinh = _make_unary_array_func("sinh")
+sinpi = _make_unary_array_func("sinpi")
+
+sqrt = _make_unary_array_func("sqrt")
+
+tan = _make_unary_array_func("tan")
+tanh = _make_unary_array_func("tanh")
+tanpi = _make_unary_array_func("tanpi")
+tgamma = _make_unary_array_func("tgamma")
+trunc = _make_unary_array_func("trunc")
+
+# no point wrapping half_ or native_
+
+# TODO: table 6.10, integer functions
+# TODO: table 6.12, clamp et al
diff --git a/pyopencl/elementwise.py b/pyopencl/elementwise.py
index 1a3f25ec60ab757d25e956bf4d071e494a585d40..0fc9aaedf2226fffb03c21745846033c3ee28bca 100644
--- a/pyopencl/elementwise.py
+++ b/pyopencl/elementwise.py
@@ -435,7 +435,7 @@ def get_frexp_kernel(context):
 def get_ldexp_kernel(context):
     return get_elwise_kernel(context,
             "float *sig, float *expt, float *z",
-            "z[i] = ldexp(sig[i], int(expt[i]))",
+            "z[i] = ldexp(sig[i], (int) expt[i])",
             "ldexp_kernel")
 
 @context_dependent_memoize
diff --git a/pyopencl/tools.py b/pyopencl/tools.py
index aca8c4c91f088aa9a3e73815718322b3b5073ee1..15ade40d93bba3192590a6cff7dd1f00bc0e4a37 100644
--- a/pyopencl/tools.py
+++ b/pyopencl/tools.py
@@ -31,6 +31,7 @@ OTHER DEALINGS IN THE SOFTWARE.
 
 import numpy
 from decorator import decorator
+import pyopencl as cl
 
 
 
@@ -57,6 +58,45 @@ def context_dependent_memoize(func, context, *args):
 
 
 
+
+def pytest_generate_tests_for_pyopencl(metafunc):
+    class ContextGetter:
+        def __init__(self, device):
+            self.device = device
+
+        def __call__(self):
+            return cl.Context([device])
+
+        def __str__(self):
+            return "<context getter for %s>" % self.device
+    if ("device" in metafunc.funcargnames
+            or "ctx_getter" in metafunc.funcargnames):
+        arg_dict = {}
+
+        for platform in cl.get_platforms():
+            if "platform" in metafunc.funcargnames:
+                arg_dict["platform"] = platform
+
+            for device in platform.get_devices():
+                if "device" in metafunc.funcargnames:
+                    arg_dict["device"] = device
+
+                if "ctx_getter" in metafunc.funcargnames:
+                    arg_dict["ctx_getter"] = ContextGetter(device)
+
+                metafunc.addcall(funcargs=arg_dict.copy(),
+                        id=", ".join("%s=%s" % (arg, value)
+                                for arg, value in arg_dict.iteritems()))
+
+    elif "platform" in metafunc.funcargnames:
+        for platform in cl.get_platforms():
+            metafunc.addcall(
+                    funcargs=dict(platform=platform),
+                    id=str(platform))
+
+
+
+
 # {{{ C code generation helpers -----------------------------------------------
 def dtype_to_ctype(dtype):
     if dtype is None:
diff --git a/test/test_array.py b/test/test_array.py
index a1f9d2634af01fdc136a6b8fe5efff1ccdb6049c..1fe64d6b7a353d32142399a42eeaff3fc54b4f8a 100644
--- a/test/test_array.py
+++ b/test/test_array.py
@@ -17,6 +17,8 @@ def have_cl():
 if have_cl():
     import pyopencl.array as cl_array
     import pyopencl as cl
+    from pyopencl.tools import pytest_generate_tests_for_pyopencl \
+            as pytest_generate_tests
 
 
 
@@ -546,41 +548,6 @@ def test_astype(ctx_getter):
 
 
 
-def pytest_generate_tests(metafunc):
-    if have_cl():
-        import pyopencl as cl
-    else:
-        # will still show "skipped" messages
-        return
-
-    if ("device" in metafunc.funcargnames
-            or "ctx_getter" in metafunc.funcargnames):
-        arg_dict = {}
-
-        for platform in cl.get_platforms():
-            if "platform" in metafunc.funcargnames:
-                arg_dict["platform"] = platform
-
-            for device in platform.get_devices():
-                if "device" in metafunc.funcargnames:
-                    arg_dict["device"] = device
-
-                if "ctx_getter" in metafunc.funcargnames:
-                    arg_dict["ctx_getter"] = lambda: cl.Context([device])
-
-                metafunc.addcall(funcargs=arg_dict.copy(),
-                        id=", ".join("%s=%s" % (arg, value) 
-                                for arg, value in arg_dict.iteritems()))
-
-    elif "platform" in metafunc.funcargnames:
-        for platform in cl.get_platforms():
-            metafunc.addcall(
-                    funcargs=dict(platform=platform),
-                    id=str(platform))
-
-
-
-
 if __name__ == "__main__":
     # make sure that import failures get reported, instead of skipping the tests.
     import pyopencl as cl
diff --git a/test/test_math.py b/test/test_math.py
new file mode 100644
index 0000000000000000000000000000000000000000..c7aabf4687d6d3997d5a37a3c5d4a249143fed1d
--- /dev/null
+++ b/test/test_math.py
@@ -0,0 +1,182 @@
+from __future__ import division
+import math
+import numpy
+import pytools.test
+
+def have_cl():
+    try:
+        import pyopencl
+        return True
+    except:
+        return False
+
+if have_cl():
+    import pyopencl.array as cl_array
+    import pyopencl as cl
+    import pyopencl.clmath as clmath
+    from pyopencl.tools import pytest_generate_tests_for_pyopencl \
+            as pytest_generate_tests
+
+
+
+
+
+sizes = [10, 128, 1024, 1<<10, 1<<13]
+
+
+
+
+def has_double_support(dev):
+    for ext in dev.extensions.split(" "):
+        if ext == "cl_khr_fp64":
+            return True
+    return False
+
+
+
+
+numpy_func_names = {
+        "asin": "arcsin",
+        "acos": "arccos",
+        "atan": "arctan",
+        }
+
+
+
+
+def make_unary_function_test(name, (a, b)=(0, 1), threshold=0):
+    def test(ctx_getter):
+        context = ctx_getter()
+        queue = cl.CommandQueue(context)
+
+        gpu_func = getattr(clmath, name)
+        cpu_func = getattr(numpy, numpy_func_names.get(name, name))
+
+        if has_double_support(context.devices[0]):
+            dtypes = [numpy.float32, numpy.float64]
+        else:
+            dtypes = [numpy.float32]
+
+        for s in sizes:
+            for dtype in dtypes:
+                args = cl_array.arange(context, queue, a, b, (b-a)/s, 
+                        dtype=numpy.float32)
+                gpu_results = gpu_func(args).get()
+                cpu_results = cpu_func(args.get())
+
+                max_err = numpy.max(numpy.abs(cpu_results - gpu_results))
+                assert (max_err <= threshold).all(), \
+                        (max_err, name, dtype)
+
+    return pytools.test.mark_test.opencl(test)
+
+
+
+
+if have_cl():
+    test_ceil = make_unary_function_test("ceil", (-10, 10))
+    test_floor = make_unary_function_test("ceil", (-10, 10))
+    test_fabs = make_unary_function_test("fabs", (-10, 10))
+    test_exp = make_unary_function_test("exp", (-3, 3), 1e-5)
+    test_log = make_unary_function_test("log", (1e-5, 1), 5e-7)
+    test_log10 = make_unary_function_test("log10", (1e-5, 1), 3e-7)
+    test_sqrt = make_unary_function_test("sqrt", (1e-5, 1), 2e-7)
+
+    test_sin = make_unary_function_test("sin", (-10, 10), 1e-7)
+    test_cos = make_unary_function_test("cos", (-10, 10), 1e-7)
+    test_asin = make_unary_function_test("asin", (-0.9, 0.9), 5e-7)
+    test_acos = make_unary_function_test("acos", (-0.9, 0.9), 5e-7)
+    test_tan = make_unary_function_test("tan", 
+            (-math.pi/2 + 0.1, math.pi/2 - 0.1), 1e-5)
+    test_atan = make_unary_function_test("atan", (-10, 10), 2e-7)
+
+    test_sinh = make_unary_function_test("sinh", (-3, 3), 1e-6)
+    test_cosh = make_unary_function_test("cosh", (-3, 3), 1e-6)
+    test_tanh = make_unary_function_test("tanh", (-3, 3), 2e-6)
+
+
+
+
+@pytools.test.mark_test.opencl
+def test_fmod(ctx_getter):
+    context = ctx_getter()
+    queue = cl.CommandQueue(context)
+
+    for s in sizes:
+        a = cl_array.arange(context, queue, s, dtype=numpy.float32)/10
+        a2 = cl_array.arange(context, queue, s, dtype=numpy.float32)/45.2 + 0.1
+        b = clmath.fmod(a, a2)
+
+        a = a.get()
+        a2 = a2.get()
+        b = b.get()
+
+        for i in range(s):
+            assert math.fmod(a[i], a2[i]) == b[i]
+
+@pytools.test.mark_test.opencl
+def test_ldexp(ctx_getter):
+    context = ctx_getter()
+    queue = cl.CommandQueue(context)
+
+    for s in sizes:
+        a = cl_array.arange(context, queue, s, dtype=numpy.float32)
+        a2 = cl_array.arange(context, queue, s, dtype=numpy.float32)*1e-3
+        b = clmath.ldexp(a,a2)
+
+        a = a.get()
+        a2 = a2.get()
+        b = b.get()
+
+        for i in range(s):
+            assert math.ldexp(a[i], int(a2[i])) == b[i]
+
+@pytools.test.mark_test.opencl
+def test_modf(ctx_getter):
+    context = ctx_getter()
+    queue = cl.CommandQueue(context)
+
+    for s in sizes:
+        a = cl_array.arange(context, queue, s, dtype=numpy.float32)/10
+        fracpart, intpart = clmath.modf(a)
+
+        a = a.get()
+        intpart = intpart.get()
+        fracpart = fracpart.get()
+
+        for i in range(s):
+            fracpart_true, intpart_true = math.modf(a[i])
+
+            assert intpart_true == intpart[i]
+            assert abs(fracpart_true - fracpart[i]) < 1e-4
+
+@pytools.test.mark_test.opencl
+def test_frexp(ctx_getter):
+    context = ctx_getter()
+    queue = cl.CommandQueue(context)
+
+    for s in sizes:
+        a = cl_array.arange(context, queue, s, dtype=numpy.float32)/10
+        significands, exponents = clmath.frexp(a)
+
+        a = a.get()
+        significands = significands.get()
+        exponents = exponents.get()
+
+        for i in range(s):
+            sig_true, ex_true = math.frexp(a[i])
+
+            assert sig_true == significands[i]
+            assert ex_true == exponents[i]
+
+
+
+
+if __name__ == "__main__":
+    # make sure that import failures get reported, instead of skipping the tests.
+    import sys
+    if len(sys.argv) > 1:
+        exec sys.argv[1]
+    else:
+        from py.test.cmdline import main
+        main([__file__])
diff --git a/test/test_wrapper.py b/test/test_wrapper.py
index 56b79369fa159d6ecf5c21507966b823a83a22c2..b61eb4b9bfd44d9d3c304d8eebbd80d43f6a4489 100644
--- a/test/test_wrapper.py
+++ b/test/test_wrapper.py
@@ -17,6 +17,8 @@ def have_cl():
 
 if have_cl():
     import pyopencl as cl
+    from pyopencl.tools import pytest_generate_tests_for_pyopencl \
+            as pytest_generate_tests
 
 
 
@@ -182,7 +184,9 @@ class TestCL:
         assert not iform.__dict__
 
     @pytools.test.mark_test.opencl
-    def test_nonempty_supported_image_formats(self, device, context):
+    def test_nonempty_supported_image_formats(self, device, ctx_getter):
+        context = ctx_getter()
+
         if device.image_support:
             assert len(cl.get_supported_image_formats(
                     context, cl.mem_flags.READ_ONLY, cl.mem_object_type.IMAGE2D)) > 0
@@ -191,7 +195,9 @@ class TestCL:
             skip("images not supported on %s" % device.name)
 
     @pytools.test.mark_test.opencl
-    def test_that_python_args_fail(self, context):
+    def test_that_python_args_fail(self, ctx_getter):
+        context = ctx_getter()
+
         prg = cl.Program(context, """
             __kernel void mult(__global float *a, float b, int c)
             { a[get_global_id(0)] *= (b+c); }
@@ -220,7 +226,9 @@ class TestCL:
         cl.enqueue_read_buffer(queue, a_buf, a_result).wait()
 
     @pytools.test.mark_test.opencl
-    def test_image_2d(self, device, context):
+    def test_image_2d(self, device, ctx_getter):
+        context = ctx_getter()
+
         if not device.image_support:
             from py.test import skip
             skip("images not supported on %s" % device)
@@ -270,39 +278,6 @@ class TestCL:
 
 
 
-def pytest_generate_tests(metafunc):
-    if have_cl():
-        import pyopencl as cl
-    else:
-        # will still show "skipped" messages
-        return
-
-    if ("device" in metafunc.funcargnames
-            or "context" in metafunc.funcargnames):
-        arg_dict = {}
-
-        for platform in cl.get_platforms():
-            if "platform" in metafunc.funcargnames:
-                arg_dict["platform"] = platform
-
-            for device in platform.get_devices():
-                if "device" in metafunc.funcargnames:
-                    arg_dict["device"] = device
-
-                if "context" in metafunc.funcargnames:
-                    arg_dict["context"] = cl.Context([device])
-
-                metafunc.addcall(funcargs=arg_dict.copy(),
-                        id=", ".join("%s=%s" % (arg, value)
-                                for arg, value in arg_dict.iteritems()))
-
-    elif "platform" in metafunc.funcargnames:
-        for platform in cl.get_platforms():
-            metafunc.addcall(
-                    funcargs=dict(platform=platform),
-                    id=str(platform))
-
-
 if __name__ == "__main__":
     # make sure that import failures get reported, instead of skipping the tests.
     import pyopencl