//@doc///////////////////////////////////////////////////////////////////////////////////////// // // SIERRA S.R.L. - Tecnología y Servicio - Departamento de Software. // //============================================================================================= // Generic Numeric Bounds traits. Fernando Cacciola. // // 23/06/2000 FLC Initial build. // 28/06/2000 FLC Separated from cvt_n. // #ifndef __RTL_NUMERIC_BOUNDS_H_ #define __RTL_NUMERIC_BOUNDS_H_ #include #include using std::numeric_limits ; namespace gmath { //---------------------------------------------------------------------------------------- // numeric_bounds<> traits. // This traits provides a consistent way to obtain different bounds // which are meaningful to arithmetic types. // It can be used as a replacement for numeric_limits::min/max // because numeric_limits::min has different meaning when applied to integer and // floating point types. // When applied to a floating point type, numeric_limits::min() returns the // SMALLEST positive number (closer to 0). // When applied to an integer type, numeric_limits::min() returns the LOWEST value // (probably negative) (closet to negative infinite) // // Provides: // // smallest(): positive value closer to 0. // biggest (): positive value closer to +INF // lowest (): value closer to -INF // highest (): value closer to +INF // // This implementation has been written and tested with Borland C++ Builder 4.0 // The auxiliary namespace and the out-of-line code is needed to avoid compiler // warnings: (*negating unsigned type, *conversion may lose significant digits and // functions containing some return statements cannot be inlined). // This auxiliary namespace provides the implementations of lowest(). // Used as an alternative to operator ? namespace numeric_bounds_aux { template struct lowest_impl {} ; // integer types (signed and unsigned). // numeric_limits::min() is OK. // for unsigned types this should be 0. // for signed types this should be -(max-1). template struct lowest_impl { static N lowest() { return std::numeric_limits::min() ; } } ; // non-integer types. // For built-in floating point types, -numeric_limits::max() si OK. // (Taken out of line because of compiler warning) template struct lowest_impl { static N lowest() ; } ; template N lowest_impl::lowest() { return -numeric_limits::max() ; } } // namespace numeric_bounds_aux template struct numeric_bounds { // smallest(): positive value closer to 0. (out-of-line because of comp warn) static N smallest() ; // biggest(): positive value closer to positive infinite. static N biggest () { return std::numeric_limits::max() ; } // lowest(): value closer to negative infinite. static N lowest() { // This hack is needed to prevent the compiler from giving a warning about // negating unsigned types and loosing precision. // You might replace this by an expression using operator ? if you like. return numeric_bounds_aux::lowest_impl::is_integer> ::lowest() ; } // highest(): value closer to positive infinite. static N highest() { return std::numeric_limits::max(); } } ; template N numeric_bounds::smallest() { return std::numeric_limits::is_integer ? N(0) : std::numeric_limits::min() ; } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- // numeric_bounds_comparison::test(). // // This static member function provides a safe way to determine if a value of type // S falls between the representable values of type T. // Calling numeric_bounds_comparison::test(s) // tests if s is in the range [lowestT,highestT]; that is, // if s>=lowestT && s<=highestT // // It returns -1,0,+1. // -1 means negative overflow (sgreaestT), // and 0 means fit. // // This test DOES NOT check for underflows due to rounding conversions. // // It can be safely used with any combinations of signed,unsigned,integer and non // integer types. // // A few traits can be specialized to add support for user defined types. // // In order to support different combinations of signed/unsigned/integer and non-integer // types, the implementation is splitted along a template dispatch. // The compiler will select the appropriate implementation according to the properties // of T and S. // namespace numeric_bounds_comp_aux { // Dispatch base. // The range checking logic consists basically in comparing s // with [lowestT,highestT] // However, since comparisons between two numbers can only be performed by // promoting both operands to a unique type, the comparison may yield incorrect // results due to implicit promotions. // In order to take care of these promotions, that is, to make sure they won't affect // the results of the comparisons, the logic is splitted in several cases which // depends on the combinations of 3 factors (last 3 template arguments) // // T_has_smaller_range plays a cental role in this logic. // Vaguely, it is true if S can represent values of type T. // For signed/unsigned combinations, since a sign mismatch is filtered out, this // flag is true when positive values of type S can represent positive values of type // T. // That is, 'int' is considered to have smaller range than 'unsigned int', // because only the positive range of 'int' is ever compared. // Floating point numbers are considered to have GREATER range than integers. // When converting an integer type to a floating point type, rounding will ocurr // if the floating point type has less significand digits, but since the result // (rounded) will fall in the whole range of floating point values, this conversion // is considered NOT TO OVERFLOW. // // T_has_smaller_range is taken from a traits based on . // All built-in types are automatically handled by the traits implementation. // User defined types can specialize this traits. // See the traits implementation below for further details. // template struct test_impl {} ; // Even when T has grater range, if S is signed but T unsigned // a negative overflow may ocurr. template struct test_impl { static int test ( S const& s ) { if ( s < S(0) ) return -1 ; // negative overflow else return 0 ; // OK } } ; // All other cases when T has greater range need no checking. template struct test_impl { static int test ( S const& ) { return 0 ; } // OK } ; // If signed/unsigned T has smaller range than unsigned S, // test for + overflow. template struct test_impl { static int test ( S const& s ) { // conversion to S is OK since T has smaller range. S maxT = (S)numeric_bounds::highest(); if ( s > maxT ) return +1 ; // positive overflow else return 0 ; // OK } } ; // If signed T has smaller range than signed S, test -/+ overflow. template struct test_impl { static int test ( S const& s ) { // Conversion to S is OK since T has smaller negative range. S minT = (S)numeric_bounds::lowest(); if ( s < minT ) return -1 ; // negative overflow else { // Conversion to S is OK since T has smaller positive range. S maxT = (S)numeric_bounds::highest(); if ( s > maxT ) return +1 ; // positive overflow else return 0 ; // OK } } } ; // If unsigned T has smaller range than signed S, test for -/+ overflow. template struct test_impl { static int test ( S const& s ) { if ( s < S(0) ) return -1 ; // negative overflow else { // conversion to S is OK since T has smaller range. S maxT = (S)numeric_bounds::highest(); if ( s > maxT ) return +1 ; // positive overflow else return 0 ; // OK } } } ; // // Below is the implementation of the flag T_has_smaller_range for // built-in types (or user defined types with conformant // arithmetic representations and a sutiable specialization of numeric_limits<>). // template struct smaller_range_impl {} ; // When both T and S are integer types, T has smaller range if and only if // T has less bits in its representation than S. template struct smaller_range_impl { static const bool T_has_smaller_range = std::numeric_limits::digits < std::numeric_limits::digits ; } ; // If T is integer but S is not integer (floating/fixed point,rational, etc...) // T is considered to have smaller range than S. template struct smaller_range_impl { static const bool T_has_smaller_range = true ; } ; // If T is not integer and S is integer, // T is considered to have bigger range than S. template struct smaller_range_impl { static const bool T_has_smaller_range = false ; } ; // If both T and S are non-integer, compare exponent bits and if they match, // mantisa bits. template struct smaller_range_impl { static const bool T_has_smaller_range = std::numeric_limits::max_exponent < std::numeric_limits::max_exponent ? true : std::numeric_limits::max_exponent == std::numeric_limits::max_exponent ? std::numeric_limits::digits < std::numeric_limits::digits : false ; } ; } // namespace numeric_bounds_comp_aux template struct numeric_bounds_comparison { // This flags is used to select an apropriate testing logic that avoids // destructive implicit promotions from T to S. // As explained above, if it is true, then the following must be meet: // Given // minT=(S)numeric_bounds::lowest() // and maxT=(S)numeric_bounds::highest() // The explicit cast to S must construct (or copy initialize) the values // minT and maxT such that the expressions: s < minT and s > maxT // should preserve the *real* arithmetic ordering of s,minT and maxT. // // NOTE: This flag MUST BE an integral constant expression. // Specializations cannot compute this as a runtime expression. // // The default implementation works with all built-in types, so it should // be specialized only for user defined types. // static const bool T_has_smaller_range = numeric_bounds_comp_aux::smaller_range_impl::is_integer, std::numeric_limits::is_integer >::T_has_smaller_range ; static int test ( S const& s ) { return numeric_bounds_comp_aux::test_impl::is_signed , std::numeric_limits::is_signed , T_has_smaller_range >::test(s) ; } } ; //---------------------------------------------------------------------------------------- } //namespace gmath. #endif // ///////////////////////////////////////////////////////////////////////////////////////////////