<limits>
The <limits>
header is part of the C++ Standard Library that provides information about the properties and limitations of arithmetic types in C++. It defines the std::numeric_limits
class template, which offers a standardized way to query various properties of arithmetic types, such as their minimum and maximum values, precision, and other characteristics.
std::numeric_limits
class templatestd::numeric_limits
#include <iostream>
#include <limits>
#include <iomanip>
int main() {
std::cout << "Properties of int:" << std::endl;
std::cout << "Min value: " << std::numeric_limits<int>::min() << std::endl;
std::cout << "Max value: " << std::numeric_limits<int>::max() << std::endl;
std::cout << "Is signed: " << std::numeric_limits<int>::is_signed << std::endl;
std::cout << "Digits: " << std::numeric_limits<int>::digits << std::endl;
std::cout << "\nProperties of double:" << std::endl;
std::cout << "Min value: " << std::numeric_limits<double>::min() << std::endl;
std::cout << "Max value: " << std::numeric_limits<double>::max() << std::endl;
std::cout << "Epsilon: " << std::numeric_limits<double>::epsilon() << std::endl;
std::cout << "Digits10: " << std::numeric_limits<double>::digits10 << std::endl;
return 0;
}
std::numeric_limits
to query properties of int
and double
types.min()
and max()
return the minimum and maximum representable values.is_signed
indicates whether the type is signed.digits
gives the number of digits in the mantissa for floating-point types or the number of bits for integer types.epsilon()
returns the smallest representable difference between two values for floating-point types.digits10
gives the number of decimal digits that can be represented without change.#include <iostream>
#include <limits>
#include <cmath>
int main() {
// Infinity
if (std::numeric_limits<float>::has_infinity) {
float pos_inf = std::numeric_limits<float>::infinity();
float neg_inf = -std::numeric_limits<float>::infinity();
std::cout << "Positive infinity: " << pos_inf << std::endl;
std::cout << "Negative infinity: " << neg_inf << std::endl;
}
// NaN (Not a Number)
if (std::numeric_limits<double>::has_quiet_NaN) {
double nan = std::numeric_limits<double>::quiet_NaN();
std::cout << "NaN: " << nan << std::endl;
std::cout << "Is NaN: " << std::isnan(nan) << std::endl;
}
// Checking if a type is integer
std::cout << "Is int integer: " << std::numeric_limits<int>::is_integer << std::endl;
std::cout << "Is float integer: " << std::numeric_limits<float>::is_integer << std::endl;
return 0;
}
has_infinity
and has_quiet_NaN
are used to check if these special values are supported.infinity()
returns the representation of positive infinity.quiet_NaN()
returns a representation of NaN (Not a Number).is_integer
is used to check if a type is an integer type.numeric_limits
for Robust Numeric Algorithms#include <iostream>
#include <limits>
#include <vector>
#include <algorithm>
template<typename T>
T safe_average(const std::vector<T>& numbers) {
if (numbers.empty()) {
return T(0);
}
T sum = 0;
size_t count = 0;
for (const T& num : numbers) {
// Check for overflow before adding
if (num > 0 && sum > std::numeric_limits<T>::max() - num) {
throw std::overflow_error("Sum would overflow");
}
if (num < 0 && sum < std::numeric_limits<T>::min() - num) {
throw std::underflow_error("Sum would underflow");
}
sum += num;
++count;
}
return sum / count;
}
int main() {
try {
std::vector<int> ints = {1, 2, 3, 4, 5};
std::cout << "Average of ints: " << safe_average(ints) << std::endl;
std::vector<double> doubles = {1.1, 2.2, 3.3, 4.4, 5.5};
std::cout << "Average of doubles: " << safe_average(doubles) << std::endl;
std::vector<int> large_ints = {std::numeric_limits<int>::max(), 1};
std::cout << "Average of large ints: " << safe_average(large_ints) << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}
safe_average
function that uses numeric_limits
to check for potential overflow or underflow.max()
and min()
to check against the type's limits before performing additions.#include <iostream>
#include <limits>
#include <iomanip>
template<typename T>
void print_float_info() {
std::cout << "Type: " << typeid(T).name() << std::endl;
std::cout << "Digits10: " << std::numeric_limits<T>::digits10 << std::endl;
std::cout << "Max exponent: " << std::numeric_limits<T>::max_exponent10 << std::endl;
std::cout << "Epsilon: " << std::numeric_limits<T>::epsilon() << std::endl;
std::cout << "Round error: " << std::numeric_limits<T>::round_error() << std::endl;
std::cout << std::endl;
}
int main() {
std::cout << std::setprecision(std::numeric_limits<long double>::digits10 + 1);
std::cout << "Float info:" << std::endl;
print_float_info<float>();
std::cout << "Double info:" << std::endl;
print_float_info<double>();
std::cout << "Long double info:" << std::endl;
print_float_info<long double>();
return 0;
}
digits10
shows the number of decimal digits that can be represented without change.max_exponent10
gives the maximum decimal exponent.epsilon()
returns the difference between 1 and the least value greater than 1 that is representable.round_error()
gives an estimate of the rounding error.numeric_limits
for Type Traits and Compile-Time Checks#include <iostream>
#include <limits>
#include <type_traits>
template<typename T>
void check_type_properties() {
std::cout << "Type: " << typeid(T).name() << std::endl;
std::cout << "Is signed: " << std::numeric_limits<T>::is_signed << std::endl;
std::cout << "Is integer: " << std::numeric_limits<T>::is_integer << std::endl;
std::cout << "Is exact: " << std::numeric_limits<T>::is_exact << std::endl;
std::cout << "Has infinity: " << std::numeric_limits<T>::has_infinity << std::endl;
std::cout << "Has quiet NaN: " << std::numeric_limits<T>::has_quiet_NaN << std::endl;
std::cout << std::endl;
}
template<typename T>
constexpr bool is_safe_integer_cast(long long value) {
return value >= std::numeric_limits<T>::min() && value <= std::numeric_limits<T>::max();
}
int main() {
check_type_properties<int>();
check_type_properties<float>();
check_type_properties<double>();
constexpr long long test_value = 1000000;
std::cout << "Is " << test_value << " safely castable to int? "
<< is_safe_integer_cast<int>(test_value) << std::endl;
std::cout << "Is " << test_value << " safely castable to short? "
<< is_safe_integer_cast<short>(test_value) << std::endl;
return 0;
}
numeric_limits
.is_signed
, is_integer
, is_exact
, etc., provide compile-time information about types.is_safe_integer_cast
function that uses numeric_limits
to check if a value can be safely cast to a given integer type.numeric_limits
can be used in template metaprogramming and compile-time computations.Portability: numeric_limits
provides a portable way to query type properties across different platforms and compilers.
Compile-Time Constants: Most numeric_limits
properties are available as compile-time constants, enabling their use in template metaprogramming and constexpr
contexts.
Custom Types: You can specialize numeric_limits
for your own types if needed.
Performance: Using numeric_limits
typically has no runtime cost as most values are compile-time constants.
C Compatibility: For C compatibility, you might still encounter macros from <climits>
and <cfloat>
in some codebases.
The <limits>
header in C++ provides the std::numeric_limits
class template, offering a standardized way to query the properties of arithmetic types:
Understanding and using <limits>
is crucial for writing numeric algorithms that are both efficient and correct across different platforms and types. It's particularly valuable in scientific computing, financial applications, and any scenario where precise control over numeric behavior is required.
By leveraging numeric_limits
, C++ programmers can write more robust, portable, and self-documenting code when dealing with arithmetic types and their limitations. This header is an essential tool for anyone working on numeric processing, low-level system programming, or any application where understanding the exact behavior of numeric types is critical.