Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds
Arrow up icon
GO TO TOP
Modern C++ Programming Cookbook

You're reading from   Modern C++ Programming Cookbook Master C++ core language and standard library features, with over 100 recipes, updated to C++20

Arrow left icon
Product type Hardcover
Published in Feb 2024
Publisher Packt
ISBN-13 9781835080542
Length 816 pages
Edition 2nd Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Marius Bancila Marius Bancila
Author Profile Icon Marius Bancila
Marius Bancila
Arrow right icon
View More author details
Toc

Table of Contents (15) Chapters Close

Preface 1. Learning Modern Core Language Features FREE CHAPTER 2. Working with Numbers and Strings 3. Exploring Functions 4. Preprocessing and Compilation 5. Standard Library Containers, Algorithms, and Iterators 6. General-Purpose Utilities 7. Working with Files and Streams 8. Leveraging Threading and Concurrency 9. Robustness and Performance 10. Implementing Patterns and Idioms 11. Exploring Testing Frameworks 12. C++ 20 Core Features 13. Other Books You May Enjoy
14. Index

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, which 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:
    // example 1
    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;
    }
    // example 2
    int range[std::numeric_limits<char>::max() + 1] = { 0 };
    // example 3
    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 bits variable 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 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>. You can find the entire list of specializations at https://en.cppreference.com/w/cpp/types/numeric_limits.

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.
  • min and max 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:

unsigned short

int

double

min=0
max=65535
bits=16
decdigits=4
integral=1
signed=0
exact=1
infinity=0
min=-2147483648
max=2147483647
bits=31
decdigits=9
integral=1
signed=1
exact=1
infinity=0
min=2.22507e-308
max=1.79769e+308
bits=53
decdigits=15
integral=0
signed=1
exact=0
infinity=1

Table 2.5: The output of print_type_properties() for unsigned short, int, and double

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
lock icon The rest of the chapter is locked
CONTINUE READING
83
Tech Concepts
36
Programming languages
73
Tech Tools
Icon Unlimited access to the largest independent learning library in tech of over 8,000 expert-authored tech books and videos.
Icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Icon 50+ new titles added per month and exclusive early access to books as they are being written.
Modern C++ Programming Cookbook
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime
Modal Close icon
Modal Close icon