This is wrong for several reasons.
What do you mean by "is an integer"?
There are many ways to interpret the assertion that some number "is an integer". Do you mean it's an integer type? If so you could use the concept std::integral<T>
, but that's obviously not what you mean.
Next is checking if a given number can be stored without loss of precision in an integer variable. That is actually a very useful definition. However, your test would fail that because input
might be much larger than can be stored even in a std::uintmax_t
.
Then you can check if a given number is actually exactly an integer. I am assuming that that is what you want to achieve here. Luckily that is not too hard, but see below.
Finally, it could be that you have done some floating point operations and want to know if the result is an integer, but due to the inexact nature of floating point it might not come out as an exact integer. In that case, the right tolerance to use actually depends on which and how many operations you did on the numbers before getting the final answer, as every operation increases the error. In any case:
Don't use epsilon
epsilon
is the smallest possible difference between floating point numbers that are in the range of 1 to 2. However, consider that input
might be much larger than 1, in which case the smallest difference between input
and floor_input
will be much larger than epsilon
. And if input
is very close to 0, the opposite will be the case.
You could scale epsilon
based on the exponent of input
, as shown in this example. But even then it is not necessary:
Use std::modf()
Due to the way the floating point number format IEEE 754 works, you can tell exactly if a given float
or double
is an integer or not. One way would be to just cast the number to an integer and compare it with the floating point version:
template<typename T>
constexpr bool is_integer(T input)
{
return static_cast<intmax_t>(input) == input;
}
Which works well until input
is larger than intmax_t
can handle. Luckily, an even better way is available by using std::modf()
, which splits a floating point value into an integer and fractional part:
template<std::floating_point T>
constexpr bool is_integer(T input)
{
T integer_part;
return std::abs(std::modf(input, &integer_part)) == 0;
}
template<std::integral T>
constexpr bool is_integer(T input)
{
return true;
}
What about other numeric types?
There are more numeric types than just integers and floating point numbers. What about std::complex
? Maybe you are using a library that provides rational numbers? Either you could try in some way to make the code even more generic such that it handles those cases, or you could just forbid those types by requiring that std::is_arithmetic_v<T>
.
Unnecessary use of if
Whenever you have a piece of code that looks like:
if (condition)
return true;
else
return false;
Just replace that with:
return condition;