Repository: numpy/numpy

Issue:
BUG: Unexpected pad value when padding an uint64 array
This seems to be an edge case, nevertheless I wanted to report this behavior. Especially because I find it kind of interesting.

### Description

When padding an array of dtype uint64 there are some cases when mode "constant" doesn't append with the correct value but instead pads with 0.

**Code to reproduce**

```python
>>> offset = 1024
>>> pad_value = np.uint64(-1024)
>>> x = np.zeros(5, dtype=np.uint64) - offset
>>> np.pad(x, 1, mode="constant", constant_values=pad_value)
array([                   0, 18446744073709551615, 18446744073709551615,
       18446744073709551615, 18446744073709551615, 18446744073709551615,
                          0], dtype=uint64)
```

**Expected Result**

```python
array([18446744073709551615, 18446744073709551615, 18446744073709551615,
       18446744073709551615, 18446744073709551615, 18446744073709551615,
       18446744073709551615], dtype=uint64)
```

There are some interesting observations to be made:

- This only happens for uint64 (uint32, uint16 and below are fine).
- This behavior is only visible if `offset` is in [1, 1024] and the argument of `np.uint64` in `pad_value` is in [-1, -1024].
- `np.pad(x, 1, mode="minimum")` (or `maximum`) works fine,  regardless of `offset`.

### Platform, versions, etc.

NumPy: 1.14.2
Operating system: Manjaro 17.1.7, x86_64 Linux 4.14.36-1-MANJARO
Python version: 3.6.5 (Anaconda, [GCC 7.2.0] on linux)


I hope this is reproduce-able and not a weird quirk of my setup. Feel free to ask for further diagnostics if you can think of any.


Discussion:
Here is another case with a wrong pad value:

```python
>>> x = np.ones(5, dtype=np.int64) * 9223372036854775765
>>> np.pad(x, 1, mode="constant", constant_values=x.min())
array([-9223372036854775808,  9223372036854775765,  9223372036854775765,
        9223372036854775765,  9223372036854775765,  9223372036854775765,
       -9223372036854775808], dtype=int64)
```
Thanks for reporting and investigating!

Just to dig a bit deeper, this ends up calling `_prepend_const` and `_append_const`, which share the problem:
```
np.lib.arraypad._prepend_const(x.copy(), 1, pad_value, 0)
# array([                   0, 18446744073709550592, 18446744073709550592,
#        18446744073709550592, 18446744073709550592, 18446744073709550592],
#       dtype=uint64)
```
The problem boils down to [this line](https://github.com/numpy/numpy/blob/master/numpy/lib/arraypad.py#L107), where the padding is constructed using:
```
(np.zeros(padshape) + val).astype(arr.dtype)
```
this creates zeros with `dtype=np.float64`, which is wrong. I think the following replacement will solve this:
```
np.full(padshape, val).astype(arr.dtype)
```

Any interest in making a PR?
Wow, thank you for the quick and, assuming this is the cause, useful answer! I didn't even think of looking into `pad`'s code. Next time I'll try that first.

> Any interest in making a PR?

Yeah, why not.  :wink: 
Great, if you're new to making a PR, have a look at the "contributing to numpy" section of the developer's guide; here, most relevant is to include a new test case - which can of course just be the one you have at the top.

Resolving patch:
diff --git a/numpy/lib/arraypad.py b/numpy/lib/arraypad.py
--- a/numpy/lib/arraypad.py
+++ b/numpy/lib/arraypad.py
@@ -100,12 +100,8 @@ def _prepend_const(arr, pad_amt, val, axis=-1):
         return arr
     padshape = tuple(x if i != axis else pad_amt
                      for (i, x) in enumerate(arr.shape))
-    if val == 0:
-        return np.concatenate((np.zeros(padshape, dtype=arr.dtype), arr),
-                              axis=axis)
-    else:
-        return np.concatenate(((np.zeros(padshape) + val).astype(arr.dtype),
-                               arr), axis=axis)
+    return np.concatenate((np.full(padshape, val, dtype=arr.dtype), arr),
+                          axis=axis)
 
 
 def _append_const(arr, pad_amt, val, axis=-1):
@@ -134,12 +130,8 @@ def _append_const(arr, pad_amt, val, axis=-1):
         return arr
     padshape = tuple(x if i != axis else pad_amt
                      for (i, x) in enumerate(arr.shape))
-    if val == 0:
-        return np.concatenate((arr, np.zeros(padshape, dtype=arr.dtype)),
-                              axis=axis)
-    else:
-        return np.concatenate(
-            (arr, (np.zeros(padshape) + val).astype(arr.dtype)), axis=axis)
+    return np.concatenate((arr, np.full(padshape, val, dtype=arr.dtype)),
+                          axis=axis)
 
 
 def _prepend_edge(arr, pad_amt, axis=-1):


Test patch:
diff --git a/numpy/lib/tests/test_arraypad.py b/numpy/lib/tests/test_arraypad.py
--- a/numpy/lib/tests/test_arraypad.py
+++ b/numpy/lib/tests/test_arraypad.py
@@ -489,6 +489,19 @@ def test_check_constant_pad_2d(self):
         )
         assert_allclose(test, expected)
 
+    def test_check_large_integers(self):
+        uint64_max = 2 ** 64 - 1
+        arr = np.full(5, uint64_max, dtype=np.uint64)
+        test = np.pad(arr, 1, mode="constant", constant_values=arr.min())
+        expected = np.full(7, uint64_max, dtype=np.uint64)
+        assert_array_equal(test, expected)
+
+        int64_max = 2 ** 63 - 1
+        arr = np.full(5, int64_max, dtype=np.int64)
+        test = np.pad(arr, 1, mode="constant", constant_values=arr.min())
+        expected = np.full(7, int64_max, dtype=np.int64)
+        assert_array_equal(test, expected)
+
 
 class TestLinearRamp(object):
     def test_check_simple(self):