std::numeric_limits::is_modulo(3)

NAME
std::numeric_limits::is_modulo - std::numeric_limits::is_modulo

Synopsis
   static const bool is_modulo; (until C++11)
   static constexpr bool is_modulo; (since C++11)
   The value of std::numeric_limits<T>::is_modulo is true for all arithmetic types T
   that
   are possible to
   (until C++11)handle overflows with modulo arithmetic, that is, if the result of
   addition, subtraction, multiplication, or division of this type would fall outside
   the range [min(), max()], the value returned by such operation differs from the
   expected value by a multiple of max()-min()+1.
   is_modulo is false for signed integer types, unless the implementation (since C++11)
   defines signed integer overflow to wrap.

Standard specializations
   T value of std::numeric_limits<T>::is_modulo
   /* non-specialized */ false
   bool false
   char implementation-defined
   signed char implementation-defined
   unsigned char true
   wchar_t implementation-defined
   char8_t (C++20) true
   char16_t (C++11) true
   char32_t (C++11) true
   short implementation-defined
   unsigned short true
   int implementation-defined
   unsigned int true
   long implementation-defined
   unsigned long true
   long long (C++11) implementation-defined
   unsigned long long (C++11) true
   float false
   double false
   long double false

Notes
   Although the C++11 standard still says "On most machines, this is true for signed
   integers.", it is a defect and has been corrected. The exact wording changed from
   C++03 to C++11 in such a way that the true value is no longer compatible with
   undefined behavior on signed integer overflow. Because of that, the implementations
   that rely on signed overflow being undefined (for optimization opportunities) now
   set is_modulo to false for signed integers. See for example GCC PR 22200.

Example
   Demonstrates the behavior of modulo types // Run this code
 #include <iostream>
 #include <type_traits>
 #include <limits>
 template<class T>
 typename std::enable_if<std::numeric_limits<T>::is_modulo>::type
     check_overflow()
 {
     std::cout << "\nmax value is " << std::numeric_limits<T>::max() << '\n'
               << "min value is " << std::numeric_limits<T>::min() << '\n'
               << "max value + 1 is " << std::numeric_limits<T>::max()+1 << '\n';
 }
 int main()
 {
     check_overflow<int>();
     check_overflow<unsigned long>();
     // check_overflow<float>(); // compile-time error, not a modulo type
 }

Possible output:
 max value is 2147483647
 min value is -2147483648
 max value + 1 is -2147483648
 max value is 18446744073709551615
 min value is 0
 max value + 1 is 0
  Defect reports
   The following behavior-changing defect reports were applied retroactively to
   previously published C++ standards.
      DR Applied to Behavior as published Correct behavior
                       is_modulo was required to true required to be false for signed
   LWG 2422 C++11 for signed integer types on integer types
                       most machines unless signed integer overflow is
                        defined to wrap

See also
   is_integer identifies integer types
   [static] (public static member constant)
   is_iec559 identifies the IEC 559/IEEE 754 floating-point types
   [static] (public static member constant)
   is_exact identifies exact types
   [static] (public static member constant)