- Modern C++ Programming Cookbook
- Marius Bancila
- 826字
- 2021-06-11 18:22:15
Limits and other properties of numeric types
Sometimes, it is necessary to know and use the minimum and maximum values that can be represented with a numeric type, such as char, int, or double. Many developers use standard C macros for this, such as CHAR_MIN/CHAR_MAX, INT_MIN/INT_MAX, and DBL_MIN/DBL_MAX. C++ provides a class template called numeric_limits with specializations for every numeric type that enables you to query the minimum and maximum value of a type. However, numeric_limits is not limited to that functionality, and offers additional constants for type property querying, such as whether a type is signed or not, how many bits it needs for representing its values, whether it can represent infinity for floating-point types, and many others. Prior to C++11, the use of numeric_limits<T> was limited because it could not be used in places where constants were needed (examples include the size of arrays and switch cases). Due to that, developers preferred to use C macros throughout their code. In C++11, that is no longer the case, as all the static members of numeric_limits<T> are now constexpr, which means they can be used everywhere a constant expression is expected.
Getting ready
The numeric_limits<T> class template is available in the namespace std in the <limits> header.
How to do it...
Use std::numeric_limits<T> to query various properties of a numeric type T:
- Use the min() and max() static methods to get the smallest and largest finite numbers of a type. The following are examples of how these could be used:
template<typename T, typename Iter> T minimum(Iter const start, Iter const end) // finds the // minimum value // in a range { T minval = std::numeric_limits<T>::max(); for (auto i = start; i < end; ++i) { if (*i < minval) minval = *i; } return minval; } int range[std::numeric_limits<char>::max() + 1] = { 0 }; switch(get_value()) { case std::numeric_limits<int>::min(): // do something break; }
- Use other static methods and static constants to retrieve other properties of a numeric type. In the following example, the variable bits is an std::bitset object that contains a sequence of bits that are necessary to represent the numerical value represented by the variable n (which is an integer):
auto n = 42; std::bitset<std::numeric_limits<decltype(n)>::digits> bits { static_cast<unsigned long long>(n) };
In C++11, there is no limitation to where std::numeric_limits<T> can be used; therefore, preferably, use it over C macros in your modern C++ code.
How it works...
The std::numeric_limits<T> class template enables developers to query properties of numeric types. Actual values are available through specializations, and the standard library provides specializations for all the built-in numeric types (char, short, int, long, float, double, and so on). In addition, third parties may provide additional implementations for other types. An example could be a numeric library that implements a bigint integer type and a decimal type and provides specializations of numeric_limits for these types (such as numeric_limits<bigint> and numeric_limits<decimal>).
The following specializations of numeric types are available in the <limits> header. Note that specializations for char16_t and char32_t are new in C++11; the others were available previously. Apart from the specializations listed ahead, the library also includes specializations for every cv-qualified version of these numeric types, and they are identical to the unqualified specialization. For example, consider the type int; there are four actual specializations (and they are identical): numeric_limits<int>, numeric_limits<const int>, numeric_limits<volatile int>, and numeric_limits<const volatile int>:
template<> class numeric_limits<bool>;
template<> class numeric_limits<char>;
template<> class numeric_limits<signed char>;
template<> class numeric_limits<unsigned char>;
template<> class numeric_limits<wchar_t>;
template<> class numeric_limits<char16_t>;
template<> class numeric_limits<char32_t>;
template<> class numeric_limits<short>;
template<> class numeric_limits<unsigned short>;
template<> class numeric_limits<int>;
template<> class numeric_limits<unsigned int>;
template<> class numeric_limits<long>;
template<> class numeric_limits<unsigned long>;
template<> class numeric_limits<long long>;
template<> class numeric_limits<unsigned long long>;
template<> class numeric_limits<float>;
template<> class numeric_limits<double>;
template<> class numeric_limits<long double>;
As mentioned earlier, in C++11, all static members of std::numeric_limits are constexpr, which means they can be used in all the places where constant expressions are needed. These have several major advantages over C++ macros:
- They are easier to remember, as the only thing you need to know is the name of the type, which you should know anyway, and not countless names of macros.
- They support types that are not available in C, such as char16_t and char32_t.
- They are the only possible solutions for templates where you don't know the type.
- Minimum and maximum are only two of the various properties of types it provides; therefore, its actual use is beyond the numeric limits shown. As a side note, for this reason, the class should have been perhaps called numeric_properties, instead of numeric_limits.
The following function template, print_type_properties(), prints the minimum and maximum finite values of the type, as well as other information:
template <typename T>
void print_type_properties()
{
std::cout
<< "min="
<< std::numeric_limits<T>::min() << '\n'
<< "max="
<< std::numeric_limits<T>::max() << '\n'
<< "bits="
<< std::numeric_limits<T>::digits << '\n'
<< "decdigits="
<< std::numeric_limits<T>::digits10 << '\n'
<< "integral="
<< std::numeric_limits<T>::is_integer << '\n'
<< "signed="
<< std::numeric_limits<T>::is_signed << '\n'
<< "exact="
<< std::numeric_limits<T>::is_exact << '\n'
<< "infinity="
<< std::numeric_limits<T>::has_infinity << '\n';
}
If we call the print_type_properties() function for unsigned short, int, and double, we will get the following output:

Please note that there is a difference between the digits and digits10 constants:
- digits represents the number of bits (excluding the sign bit if present) and padding bits (if any) for integral types and the number of bits of the mantissa for floating-point types.
- digits10 is the number of decimal digits that can be represented by a type without a change. To understand this better, let's consider the case of unsigned short. This is a 16-bit integral type. It can represent numbers between 0 and 65,536. It can represent numbers up to five decimal digits, 10,000 to 65,536, but it cannot represent all five decimal digit numbers, as numbers from 65,537 to 99,999 require more bits. Therefore, the largest numbers that it can represent without requiring more bits have four decimal digits (numbers from 1,000 to 9,999). This is the value indicated by digits10. For integral types, it has a direct relationship to constant digits; for an integral type, T, the value of digits10 is std::numeric_limits<T>::digits * std::log10(2).
It's worth mentioning that the standard library types that are aliases of arithmetic types (such as std::size_t) may also be inspected with std::numeric_limits. On the other hand, other standard types that are not arithmetic types, such as std::complex<T> or std::nullptr_t, do not have std::numeric_limits specializations.
See also
- Converting between numeric and string types to learn how to convert between numbers and strings
- Angular UI Development with PrimeNG
- SQL for Data Analytics
- C語言程序設計案例式教程
- Kali Linux Wireless Penetration Testing Beginner's Guide(Third Edition)
- 微信小程序開發解析
- 網站構建技術
- R Deep Learning Cookbook
- Kotlin從基礎到實戰
- Unity 2D Game Development Cookbook
- 計算機應用基礎實踐教程
- C++從入門到精通(第5版)
- 深入淺出React和Redux
- 深入淺出Go語言編程
- LabVIEW入門與實戰開發100例(第4版)
- Apache Solr for Indexing Data