Skip to content

Commit

Permalink
ENH: Add integer.is_integer
Browse files Browse the repository at this point in the history
Match `int.is_integer`, which was added in python/cpython#6121
  • Loading branch information
Bas van Beek committed Aug 31, 2021
1 parent 5d86d8c commit 9f11564
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 2 deletions.
1 change: 1 addition & 0 deletions numpy/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -3190,6 +3190,7 @@ class integer(number[_NBit1]): # type: ignore
__args: Union[L[0], Tuple[()], Tuple[L[0]]] = ...,
) -> int: ...
def tolist(self) -> int: ...
def is_integer(self) -> L[True]: ...
def __index__(self) -> int: ...
__truediv__: _IntTrueDiv[_NBit1]
__rtruediv__: _IntTrueDiv[_NBit1]
Expand Down
12 changes: 12 additions & 0 deletions numpy/core/_add_newdocs_scalars.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,18 @@ def add_newdoc_for_scalar_type(obj, fixed_aliases, doc):
See :ref:`arrays.datetime` for more information.
""")

add_newdoc('numpy.core.numerictypes', "integer", ('is_integer',
f"""
integer.is_integer() -> bool
Return ``True`` if the number is finite with integral value.
>>> np.int64(-2).is_integer()
True
>>> np.uint32(5).is_integer()
True
"""))

# TODO: work out how to put this on the base class, np.floating
for float_name in ('half', 'single', 'double', 'longdouble'):
add_newdoc('numpy.core.numerictypes', float_name, ('as_integer_ratio',
Expand Down
18 changes: 17 additions & 1 deletion numpy/core/src/multiarray/scalartypes.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -1937,6 +1937,11 @@ static PyObject *
}
/**end repeat**/

static PyObject *
integer_is_integer(PyObject *self) {
Py_RETURN_TRUE;
}

/*
* need to fill in doc-strings for these methods on import -- copy from
* array docstrings
Expand Down Expand Up @@ -2195,7 +2200,7 @@ static PyMethodDef @name@type_methods[] = {
/**end repeat**/

/**begin repeat
* #name = integer,floating, complexfloating#
* #name = floating, complexfloating#
*/
static PyMethodDef @name@type_methods[] = {
/* Hook for the round() builtin */
Expand All @@ -2206,6 +2211,17 @@ static PyMethodDef @name@type_methods[] = {
};
/**end repeat**/

static PyMethodDef integertype_methods[] = {
/* Hook for the round() builtin */
{"__round__",
(PyCFunction)integertype_dunder_round,
METH_VARARGS | METH_KEYWORDS, NULL},
{"is_integer",
(PyCFunction)integer_is_integer,
METH_NOARGS, NULL},
{NULL, NULL, 0, NULL} /* sentinel */
};

/**begin repeat
* #name = half,float,double,longdouble#
*/
Expand Down
6 changes: 5 additions & 1 deletion numpy/core/tests/test_scalar_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,19 +104,23 @@ def test_roundtrip(self, ftype, frac_vals, exp_vals):
assert_equal(nf / df, f, "{}/{}".format(n, d))


@pytest.mark.parametrize("code", np.typecodes["Float"])
class TestIsInteger:
@pytest.mark.parametrize("str_value", ["inf", "nan"])
@pytest.mark.parametrize("code", np.typecodes["Float"])
def test_special(self, code: str, str_value: str) -> None:
cls = np.dtype(code).type
value = cls(str_value)
assert not value.is_integer()

@pytest.mark.parametrize(
"code", np.typecodes["Float"] + np.typecodes["AllInteger"]
)
def test_true(self, code: str) -> None:
float_array = np.arange(-5, 5).astype(code)
for value in float_array:
assert value.is_integer()

@pytest.mark.parametrize("code", np.typecodes["Float"])
def test_false(self, code: str) -> None:
float_array = np.arange(-5, 5).astype(code)
float_array *= 1.1
Expand Down
2 changes: 2 additions & 0 deletions numpy/typing/tests/data/reveal/scalars.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,5 @@
if sys.version_info >= (3, 9):
reveal_type(f8.__ceil__()) # E: int
reveal_type(f8.__floor__()) # E: int

reveal_type(i8.is_integer()) # E: Literal[True]

0 comments on commit 9f11564

Please sign in to comment.