Repository: numpy/numpy

Issue:
BUG: exp and log cast float16 to float32
<!-- Please describe the issue in detail here, and fill in the fields below -->
After #13134 is merged, `np.exp` and `np.log` return float32 arrays for float16 inputs.  Is the change intended?  I could not find docs for it (https://www.numpy.org/devdocs/reference/generated/numpy.exp.html), while it is documented that `np.sum` casts ints to the default platform integer.

### Reproducing code example:

<!-- A short code example that reproduces the problem/missing feature. It should be
self-contained, i.e., possible to run as-is via 'python myproblem.py' -->

```python
import numpy as np
print(np.exp(np.array([1, 2], np.float16)).dtype)  # => float32
print(np.log(np.array([1, 2], np.float16)).dtype)  # => float32
```

<!-- Remove these sections for a feature request -->

<!-- ### Error message: -->

<!-- If you are reporting a segfault please include a GDB traceback, which you
can generate by following
https://github.com/numpy/numpy/blob/master/doc/source/dev/development_environment.rst#debugging -->

<!-- Full error message, if any (starting from line Traceback: ...) -->

### Numpy/Python version information:

<!-- Output from 'import sys, numpy; print(numpy.__version__, sys.version)' -->
```
1.17.0.dev0+64b3e67 3.7.1 (default, Nov  6 2018, 21:02:07)
[Clang 10.0.0 (clang-1000.10.44.4)]
```



Discussion:
Looks like an unintended change. @r-devulap @mattip Thoughts? Maybe we need some tests...
Adding tests and looking for a fix
Looking into it .. 
I am sure you already got it, but it will be because of the linear search of the loops, if the "f,f->f" loops are registered first, they are found instead of the "e,e->e" loops.
Sorry for the spam. (compare also `np.sqrt` which currently has a funny loop duplication, but not this issue).
I thought changing the type to `TD('f', astype={'e':'f'}, simd=[('avx2', 'f'), ('avx512f', 'f')])` (adding the astype kwarg) would have solved it, but the test is still failing. Somehow the `simd` part is not respecting the `astype`
Got it, need to **not** use simd on 'e' which makes sense

Resolving patch:
diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py
--- a/numpy/core/code_generators/generate_umath.py
+++ b/numpy/core/code_generators/generate_umath.py
@@ -697,6 +697,7 @@ def english_upper(s):
     Ufunc(1, 1, None,
           docstrings.get('numpy.core.umath.exp'),
           None,
+          TD('e', f='exp', astype={'e':'f'}),
           TD('f', simd=[('avx2', 'f'), ('avx512f', 'f')]),
           TD(inexact, f='exp', astype={'e':'f'}),
           TD(P, f='exp'),
@@ -719,6 +720,7 @@ def english_upper(s):
     Ufunc(1, 1, None,
           docstrings.get('numpy.core.umath.log'),
           None,
+          TD('e', f='log', astype={'e':'f'}),
           TD('f', simd=[('avx2', 'f'), ('avx512f', 'f')]),
           TD(inexact, f='log', astype={'e':'f'}),
           TD(P, f='log'),


Test patch:
diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py
--- a/numpy/core/tests/test_ufunc.py
+++ b/numpy/core/tests/test_ufunc.py
@@ -1936,3 +1936,31 @@ def test_nat_is_not_inf(self, nat):
             assert not np.isinf(nat)
         except TypeError:
             pass  # ok, just not implemented
+
+
+@pytest.mark.parametrize('ufunc', [getattr(np, x) for x in dir(np)
+                                if isinstance(getattr(np, x), np.ufunc)])
+def test_ufunc_types(ufunc):
+    '''
+    Check all ufuncs that the correct type is returned. Avoid
+    object and boolean types since many operations are not defined for
+    for them.
+
+    Choose the shape so even dot and matmul will succeed
+    '''
+    for typ in ufunc.types:
+        # types is a list of strings like ii->i
+        if 'O' in typ or '?' in typ:
+            continue
+        inp, out = typ.split('->')
+        args = [np.ones((3, 3), t) for t in inp]
+        with warnings.catch_warnings(record=True):
+            warnings.filterwarnings("always")
+            res = ufunc(*args)
+        if isinstance(res, tuple):
+            outs = tuple(out)
+            assert len(res) == len(outs)
+            for r, t in zip(res, outs):
+                assert r.dtype == np.dtype(t)
+        else:
+            assert res.dtype == np.dtype(out)