I wrote a short function to count the number of digits (integer portion) of a value.
template <typename T>
static size_t digits(T num) {
auto is_fraction_of_one = [](T num) {
return (num > 0 && num < 1) || (num > -1 && num < 0);
};
if (num == 0 || is_fraction_of_one(num))
return 1;
return std::is_unsigned_v<T> ? \
static_cast<size_t>(std::log10(num)) + size_t(1) : \
static_cast<size_t>(std::log10(num < 0 ? -num : num)) + size_t(1);
}
If 0 <= abs(num) < 1
it returns 1, otherwise it computes the number of digits via std::log10(num)+1
.
The code works as-is, but the compiler (Windows VC++) warns:
warning C4146: unary minus operator applied to unsigned type, result still unsigned
which is fair, since it doesn't recognize that the -num
part in the last line will only execute when T
is signed.
If I wanted rewrite the code to prevent this warning, how could I do it?
Since std::is_unsigned_v is constexpr
, I feel like I should be able to split the code into two separate template functions: one for when T
is signed, and another for when it is not. But I can't figure out how to do it properly.
Note:
I realize I could shorten the code, and prevent the warning by casting num
to a double
(since std::log10
will do that anyway without a template arg):
template <typename T>
static size_t digits(T num) {
double dnum = std::fabs(static_cast<double>(num));
if (dnum >= 0.0 && dnum < 10.0)
return 1;
return static_cast<size_t>(std::log10(dnum)) + size_t(1);
}
but I'm asking the question primarily for my own education. Also, I do have some other functions which would more aptly benefit from the solution (whatever that may be).