numeric_limits
numeric_limits
is a monolithic class template designed for user-extension through template specialization. Unfortunately this means any attempt to extend the interface by the standard renders all existing user specializations invalid. This problem could have been avoided if each trait in numeric_limits had been declared as its own template, such as with the type traits library.
Break the monolithic numeric_limits
class template apart into a lot of individual trait templates, so that new traits can be added easily. For backwards compatibility, maintain the existing numeric_limits
template and require the generic versions of the new traits templates to forward all calls back to the existing interface. This preserves compatibility with existing user specializations.
Remove any extensions to the numeric_limits
interface added since 2003, and support those features only through the new interface. This ensures existing user specializations remain valid and complete.
The obvious similarity is that both sets of traits are implemented as metafunctions.
The key difference is semantic - numeric_traits are designed to be customised by users for their own types, where type_traits are reserved to the implementation. To help distinguish the two, numeric_traits are implemented in their own namespace.
This proposal remains 100% compatible with existing code, explicit specializations of the full numeric_limits
template for user-defined types remains valid and conforming while any code using the original template is guaranteed to get the same answer. It provides a way forward for extensions in the future and a more flexible framework that can be used now.
While there is a valid argument that a type truly is not correctly supported in C++0x until the new numeric_limits
APIs are implemented, it is not always possible for a user to update their third party libraries. By moving the customization outside the monolithic template, users can support these libraries without intruding directly into their headers.
Ammend the following standard text as highlighted.
namespace std { template<class T> class numeric_limits; enum float_round_style; enum float_denorm_style; 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<char16_t>; template<> class numeric_limits<char32_t>; template<> class numeric_limits<wchar_t>; template<> class numeric_limits<short>; template<> class numeric_limits<int>; template<> class numeric_limits<long>; template<> class numeric_limits<long long>; template<> class numeric_limits<unsigned short>; template<> class numeric_limits<unsigned int>; template<> class numeric_limits<unsigned long>; template<> class numeric_limits<unsigned long long>; template<> class numeric_limits<float>; template<> class numeric_limits<double>; template<> class numeric_limits<long double>; namespace numeric_traits { template<class T> class min; template<class T> class max; template<class T> class lowest; template<class T> class digits; template<class T> class digits10; template<class T> class max_digits10; template<class T> class is_signed; template<class T> class is_integer; template<class T> class is_exact; template<class T> class radix; template<class T> class epsilon; template<class T> class round_error; template<class T> class min_exponent; template<class T> class min_exponent10; template<class T> class max_exponent; template<class T> class max_exponent10; template<class T> class has_infinity; template<class T> class has_quiet_NaN; template<class T> class has_signaling_NaN; template<class T> class float_denorm_style has_denorm; template<class T> class has_denorm_loss; template<class T> class infinity; template<class T> class quiet_NaN; template<class T> class signaling_NaN; template<class T> class denorm_min; template<class T> class is_iec559; template<class T> class is_bounded; template<class T> class is_modulo; template<class T> class traps; template<class T> class tinyness_before; template<class T> class round_style; } }
namespace std { template<class T> class numeric_limits { public: static constexpr bool is_specialized = false; static constexpr T min() throw(); static constexpr T max() throw();static constexpr T lowest() throw();static constexpr int digits = 0; static constexpr int digits10 = 0;static constexpr int max_digits10 = 0;static constexpr bool is_signed = false; static constexpr bool is_integer = false; static constexpr bool is_exact = false; static constexpr int radix = 0; static constexpr T epsilon() throw(); static constexpr T round_error() throw(); static constexpr int min_exponent = 0; static constexpr int min_exponent10 = 0; static constexpr int max_exponent = 0; static constexpr int max_exponent10 = 0; static constexpr bool has_infinity = false; static constexpr bool has_quiet_NaN = false; static constexpr bool has_signaling_NaN = false; static constexpr float_denorm_style has_denorm = denorm_absent; static constexpr bool has_denorm_loss = false; static constexpr T infinity() throw(); static constexpr T quiet_NaN() throw(); static constexpr T signaling_NaN() throw(); static constexpr T denorm_min() throw(); static constexpr bool is_iec559 = false; static constexpr bool is_bounded = false; static constexpr bool is_modulo = false; static constexpr bool traps = false; static constexpr bool tinyness_before = false; static constexpr float_round_style round_style = round_toward_zero; }; template<class T> class numeric_limits<const T>; template<class T> class numeric_limits<volatile T>; template<class T> class numeric_limits<const volatile T>; }
The default numeric_limits<T>
template shall have all members, but with 0 or false values.
The value of each member of a specialization of numeric_limits
on a cv-qualified type cv T
shall be equal to the
value of the corresponding member of the specialization on the unqualified type T.
static constexpr T min() throw();
Minimum finite value.
For floating types with denormalization, returns the minimum positive normalized value.
Meaningful for all specializations in which is_bounded != false
, or is_bounded == false && is_signed
== false
.
Maximum finite value.
Meaningful for all specializations in which is_bounded != false
.
static constexpr T lowest() throw();
A finite value x such that there is no other finite value y where y < x
.
Meaningful for all specializations in which is_bounded != false
.
static constexpr int digits;
Number of radix
digits that can be represented without change.
For integer types, the number of non-sign bits in the representation.
For floating point types, the number of radix
digits in the mantissa.
static constexpr int digits10;
Number of base 10 digits that can be represented without change.
Meaningful for all specializations in which is_bounded != false
.
static constexpr int max_digits10;
Number of base 10 digits required to ensure that values which differ are always differentiated.
Meaningful for all floating point types.
All members shall be provided for all specializations. However, many values are only required to be meaningful under
certain conditions (for example, epsilon()
is only meaningful if is_integer
is false). Any value that is not
"meaningful" shall be set to 0 or false.
Example:
namespace std { template<> class numeric_limits<float> { public: static constexpr bool is_specialized = true; inline static constexpr float min() throw() { return 1.17549435E-38F; } inline static constexpr float max() throw() { return 3.40282347E+38F; }inline static constexpr float lowest() throw() { return -3.40282347E+38F; }static constexpr int digits = 24;static constexpr int digits10 = 6;static constexpr int max_digits10 = 9; static constexpr bool is_signed = true; static constexpr bool is_integer = false; static constexpr bool is_exact = false; static constexpr int radix = 2; inline static constexpr float epsilon() throw() { return 1.19209290E-07F; } inline static constexpr float round_error() throw() { return 0.5F; } static constexpr int min_exponent = -125; static constexpr int min_exponent10 = - 37; static constexpr int max_exponent = +128; static constexpr int max_exponent10 = + 38; static constexpr bool has_infinity = true; static constexpr bool has_quiet_NaN = true; static constexpr bool has_signaling_NaN = true; static constexpr float_denorm_style has_denorm = denorm_absent; static constexpr bool has_denorm_loss = false; inline static constexpr float infinity() throw() { return ...; } inline static constexpr float quiet_NaN() throw() { return ...; } inline static constexpr float signaling_NaN() throw() { return ...; } inline static constexpr float denorm_min() throw() { return min(); } static constexpr bool is_iec559 = true; static constexpr bool is_bounded = true; static constexpr bool is_modulo = false; static constexpr bool traps = true; static constexpr bool tinyness_before = true; static constexpr float_round_style round_style = round_to_nearest; }; }
-end example
The specialization for bool
shall be provided as follows:
namespace std { template<> class numeric_limits<bool> { public: static constexpr bool is_specialized = true; static constexpr bool min() throw() { return false; } static constexpr bool max() throw() { return true; }static constexpr bool lowest() throw() { return false; }static constexpr int digits = 1;static constexpr int digits10 = 0;static constexpr int max_digits10 = 0; static constexpr bool is_signed = false; static constexpr bool is_integer = true; static constexpr bool is_exact = true; static constexpr int radix = 2; static constexpr bool epsilon() throw() { return 0; } static constexpr bool round_error() throw() { return 0; } static constexpr int min_exponent = 0; static constexpr int min_exponent10 = 0; static constexpr int max_exponent = 0; static constexpr int max_exponent10 = 0; static constexpr bool has_infinity = false; static constexpr bool has_quiet_NaN = false; static constexpr bool has_signaling_NaN = false; static constexpr float_denorm_style has_denorm = denorm_absent; static constexpr bool has_denorm_loss = false; static constexpr bool infinity() throw() { return 0; } static constexpr bool quiet_NaN() throw() { return 0; } static constexpr bool signaling_NaN() throw() { return 0; } static constexpr bool denorm_min() throw() { return 0; } static constexpr bool is_iec559 = false; static constexpr bool is_bounded = true; static constexpr bool is_modulo = false; static constexpr bool traps = false; static constexpr bool tinyness_before = false; static constexpr float_round_style round_style = round_toward_zero; }; }
The following text is all new, so to aid clarity no insert or delete markup is used.
Numeric traits provide an extensible mechanism to describe the properties of value types. This scheme is more extensible than numeric_limits
as each trait can be customized separately without specializing the whole template. New traits can be added without invalidating existing templates. For compatiblility, all traits with an analogue in numeric_limits
are required to forward to that template.
namespace std { namespace numeric_traits { template<class T> class min { static constexpr T value() { return numeric_limits<T>::min(); } }; template<class T> class max { static constexpr T value() { return numeric_limits<T>::max(); } }; template<class T> class digits : integral_constant<int, numeric_limits<T>::digits()> {}; template<class T> class max_digits10 : integral_constant<int, numeric_limits<T>::max_digits10()> {}; template<class T> class is_signed : integral_constant<bool, numeric_limits<T>::is_signed()> {}; template<class T> class is_integer : integral_constant<bool, numeric_limits<T>::is_integer()> {}; template<class T> class is_exact : integral_constant<bool, numeric_limits<T>::is_exact()> {}; template<class T> class radix : integral_constant<int, numeric_limits<T>::radix()> {}; template<class T> class epsilon { static constexpr T value() { return numeric_limits<T>::epsilon(); } }; template<class T> class round_error { static constexpr T value() { return numeric_limits<T>::round_error(); } }; template<class T> class min_exponent : integral_constant<int, numeric_limits<T>::min_exponent()> {}; template<class T> class min_exponent10 : integral_constant<int, numeric_limits<T>::min_exponent10()> {}; template<class T> class max_exponent : integral_constant<int, numeric_limits<T>::max_exponent()> {}; template<class T> class max_exponent10 : integral_constant<int, numeric_limits<T>::max_exponent10()> {}; template<class T> class has_infinity : integral_constant<bool, numeric_limits<T>::has_infinity()> {}; template<class T> class has_quiet_NaN : integral_constant<bool, numeric_limits<T>::has_quiet_NaN()> {}; template<class T> class has_signaling_NaN : integral_constant<bool, numeric_limits<T>::has_signaling_NaN()> {} template<class T> class has_denorm : integral_constant<float_denorm_style, numeric_limits<T>::has_denorm()> {}; template<class T> class has_denorm_loss : integral_constant<bool, numeric_limits<T>::has_denorm_loss()> {}; template<class T> class infinity { static constexpr T value() { return numeric_limits<T>::infinity(); } }; template<class T> class quiet_NaN { static constexpr T value() { return numeric_limits<T>::quiet_NaN(); } }; template<class T> class signaling_NaN { static constexpr T value() { return numeric_limits<T>::signaling_NaN(); } }; template<class T> class denorm_min { static constexpr T value() { return numeric_limits<T>::denorm_min(); } }; template<class T> class is_iec559 : integral_constant<bool, numeric_limits<T>::is_iec559()> {}; template<class T> class is_bounded : integral_constant<bool, numeric_limits<T>::is_bounded()> {}; template<class T> class is_modulo : integral_constant<bool, numeric_limits<T>::is_modulo()> {}; template<class T> class traps : integral_constant<bool, numeric_limits<T>::traps()> {}; template<class T> class tinyness_before : integral_constant<bool, numeric_limits<T>::tinyness_before()> {}; template<class T> class round_style : integral_constant<float_round_style, numeric_limits<T>::round_style()> {}; } }
numeric_traits
[numeric.traits.lowest]template<class T> class lowest { static constexpr T value(); };
Requires: T is a bounded type, such that is_bounded<T>::value() != false
.
Returns: value()
returns a finite value x such that there is no other finite value y where y < x
.
numeric_traits
[numeric.traits.digits10]template<class T> class digits10 : integral_constant<int, value> {};
Requires: T is a bounded type, such that is_bounded<T>::value() != false
.
Returns: value shall be the number of base 10 digits that can be represented by T without change.