// bdlb_float.h -*-C++-*- // ---------------------------------------------------------------------------- // NOTICE // // This component is not up to date with current BDE coding standards, and // should not be used as an example for new development. // ---------------------------------------------------------------------------- #ifndef INCLUDED_BDLB_FLOAT #define INCLUDED_BDLB_FLOAT #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide floating-point classification types and functions. // //@CLASSES: // bdlb::Float: namespace for floating-point classification types and functions // //@DESCRIPTION: This component defines a utility 'struct', 'bdlb::Float', that // provides functions analogous to C99 '<math.h>' library macros such as // 'isinf' and 'isnan' that return whether a 'float' or 'double' value is // infinite or not-a-number, respectively. These macros are not available in // C++98 and so are provided as functions in this component. // ///Classification of Floating-Point Numbers ///---------------------------------------- // Floating-point numbers are used to represent a subset of the set of real // numbers. The C++ 'float' and 'double' types are used to hold floating-point // numbers, with 'float' having (much) less precision than 'double'. Floating // point numbers can be classified into the following disjoint sets: //.. // Zero positive and negative zero // Normal full-precision, non-zero, normal numbers // Subnormal reduced-precision numbers with small absolute values // Infinity positive and negative infinities // NaN not a number //.. // A NaN value can be further classified into two disjoint subsets: //.. // Signaling NaN invalid values that raises a signal in computations // Quiet NaN indeterminate values that propagate through computations //.. // Note that not all platforms support signaling NaNs and that even those that // do often require a specific action to enable signals on floating-point // traps. Signaling NaNs are never the result of a normal floating-point // operation. They are most often used as sentinels to detect the use of a // value that has not yet been computed. // // Quiet NaNs are the result of certain operations where the result is not // defined mathematically. If an expression that results in a (quiet) NaN is // used in a subsequent computation, the result is usually also a (quiet) NaN. // // On platforms that implement the IEEE 754 standard for floating-point // arithmetic, the following conditions result in non-normal floating-point // values. In the following table, "NaN" always refers to a quiet NaN: //.. // Condition Result // ------------------- ----------------- // Overflow Infinity // Underflow Subnormal or Zero // Normal / Infinity Zero // Infinity * Infinity Infinity // nonzero / Zero Infinity // Infinity + Infinity Infinity // Zero / Zero NaN // Infinity - Infinity NaN // Infinity / Infinity NaN // Infinity * Zero NaN //.. // Note that the operations that result in Infinity follow the normal rules for // sign propagation, e.g., -5.0 / 0.0 results in negative Infinity. // ///Future Enhancements ///------------------- // At present, this component works with 'float' and 'double' numbers. In the // future, it will also work with 'long double'. Note that casting a // 'long double' to 'double' before applying these classification functions // will not always yield correct results. For example, a large 'long double' // may get demoted to an infinite 'double'. Similarly, a small 'long double' // may get demoted to a subnormal or zero 'double'. // ///Thread Safety ///------------- // Any of the functions in this component may safely be called simultaneously // from multiple threads, even with the same arguments. // ///Usage ///----- // This section illustrates intended use of this component. // ///Example 1: Basic Syntax ///- - - - - - - - - - - - // On platforms that implement the IEEE 754 standard for floating-point // arithmetic, dividing a positive number by zero yields positive infinity and // dividing a negative number by zero yields negative infinity. The result of // division by zero will therefore be detected as infinite by the 'isInfinite' // method and classified as infinity by the 'classify' and 'classifyFine' // methods in this component: //.. // double zero = 0.0; // double a = 2.3 / zero; // double b = -0.55 / zero; // assert(true == bdlb::Float::isZero(zero)); // assert(true == bdlb::Float::isInfinite(a)); // assert(true == bdlb::Float::isInfinite(b)); // assert(bdlb::Float::k_ZERO == bdlb::Float::classify(zero)); // assert(bdlb::Float::k_INFINITE == bdlb::Float::classify(a)); // assert(bdlb::Float::k_INFINITE == bdlb::Float::classify(b)); // assert(bdlb::Float::k_POSITIVE_INFINITY == bdlb::Float::classifyFine(a)); // assert(bdlb::Float::k_NEGATIVE_INFINITY == bdlb::Float::classifyFine(b)); //.. // Note that the sign rules apply as usual: //.. // double nzero = -0.0; // double bn = -0.55 / nzero; // assert(bdlb::Float::k_POSITIVE_INFINITY == bdlb::Float::classifyFine(bn)); //.. // The result of multiplying infinity by infinity is also infinity, but the // result of multiplying infinity by zero is an indeterminate value (quiet // NaN): //.. // double c = a * b; // double d = a * zero; // assert(true == bdlb::Float::isInfinite(c)); // assert(false == bdlb::Float::isInfinite(d)); // assert(true == bdlb::Float::isNan(d)); // assert(true == bdlb::Float::isQuietNan(d)); // assert(false == bdlb::Float::isSignalingNan(d)); //.. // Quiet NaNs propagate such that further calculations also yield quiet NaNs: //.. // double g = d - 3.4e12; // assert(false == bdlb::Float::isInfinite(g)); // assert(true == bdlb::Float::isNan(g)); // assert(true == bdlb::Float::isQuietNan(g)); //.. // We can also detect whether a value has full precision (normal) or is so // small (close to zero) that precision has been lost (subnormal): //.. // double e = -10.0 / 11.0; // Full precision // double f = e / DBL_MAX; // Lost precision // assert(true == bdlb::Float::isNormal(e)); // assert(false == bdlb::Float::isSubnormal(e)); // assert(false == bdlb::Float::isNormal(f)); // assert(true == bdlb::Float::isSubnormal(f)); // assert(bdlb::Float::k_NORMAL == bdlb::Float::classify(e)); // assert(bdlb::Float::k_SUBNORMAL == bdlb::Float::classify(f)); //.. // The 'Classification' enumeration type is designed so that each // classification occupies a separate bit. This makes it easy to test for // multiple classifications in one test. For example, if we are interested in // very that zero or denormalized (i.e., very small), we can detect both // conditions with a single mask: //.. // const int SMALL_MASK = bdlb::Float::k_ZERO | bdlb::Float::k_SUBNORMAL; // assert(0 != (SMALL_MASK & bdlb::Float::classify(0.0))); // assert(0 != (SMALL_MASK & bdlb::Float::classify(f))); // assert(0 == (SMALL_MASK & bdlb::Float::classify(e))); //.. // Note, however, that although we can create a mask with several // classification bits, a single number belongs to only one classification and // the return value of 'classify' will have only one bit set at a time. #include <bdlscm_version.h> namespace BloombergLP { namespace bdlb { // ============ // struct Float // ============ struct Float { // Namespace for floating-point classification types and functions. // TYPES enum Classification { // Basic classifications for floating-point numbers. Every // floating-point number belongs to exactly one of these // classifications. However, the enumerated values have disjoint // bit-patterns to make it easy to create a "set" of classifications // using bit-wise OR. k_ZERO = 0x01, // positive or negative zero k_NORMAL = 0x02, // full-precision, non-zero, normal number k_SUBNORMAL = 0x04, // reduced-precision numb with a small abs value k_INFINITE = 0x08, // positive or negative infinity k_NAN = 0x10 // not a number #ifndef BDE_OMIT_INTERNAL_DEPRECATED , BDES_ZERO = k_ZERO , BDES_NORMAL = k_NORMAL , BDES_SUBNORMAL = k_SUBNORMAL , BDES_INFINITE = k_INFINITE , BDES_NAN = k_NAN #endif // BDE_OMIT_INTERNAL_DEPRECATED }; enum FineClassification { // Fine-grained classifications for floating-point numbers that // distinguish positive numbers from negative numbers and quiet NaNs // from signaling NaNs. Every floating-point number belongs to exactly // one of these classifications. k_NEGATIVE = 0x8000, // Bit for negative floats k_SIGNALING = 0x4000, // Bit for signaling NaNs k_POSITIVE_INFINITY = k_INFINITE, k_NEGATIVE_INFINITY = k_INFINITE | k_NEGATIVE, k_QNAN = k_NAN, k_SNAN = k_NAN | k_SIGNALING, k_POSITIVE_NORMAL = k_NORMAL, k_NEGATIVE_NORMAL = k_NORMAL | k_NEGATIVE, k_POSITIVE_SUBNORMAL = k_SUBNORMAL, k_NEGATIVE_SUBNORMAL = k_SUBNORMAL | k_NEGATIVE, k_POSITIVE_ZERO = k_ZERO, k_NEGATIVE_ZERO = k_ZERO | k_NEGATIVE #ifndef BDE_OMIT_INTERNAL_DEPRECATED , BDES_NEGATIVE = k_NEGATIVE , BDES_SIGNALING = k_SIGNALING , BDES_POSITIVE_INFINITY = k_POSITIVE_INFINITY , BDES_NEGATIVE_INFINITY = k_NEGATIVE_INFINITY , BDES_QNAN = k_QNAN , BDES_SNAN = k_SNAN , BDES_POSITIVE_NORMAL = k_POSITIVE_NORMAL , BDES_NEGATIVE_NORMAL = k_NEGATIVE_NORMAL , BDES_POSITIVE_SUBNORMAL = k_POSITIVE_SUBNORMAL , BDES_NEGATIVE_SUBNORMAL = k_NEGATIVE_SUBNORMAL , BDES_POSITIVE_ZERO = k_POSITIVE_ZERO , BDES_NEGATIVE_ZERO = k_NEGATIVE_ZERO #endif // BDE_OMIT_INTERNAL_DEPRECATED }; // CLASS METHODS static Classification classify(float number); static Classification classify(double number); // Return the coarse classification ('k_ZERO', 'k_NORMAL', // 'k_SUBNORMAL', 'k_INFINITE', or 'k_NAN) for the specified floating // point 'number'. This function has the same functionality as the // 'fpclassify' macro in C99. static FineClassification classifyFine(float number); static FineClassification classifyFine(double number); // Return the fine-grained classification for the specified floating // point 'number'. For positive numbers and quiet NaNs, the // 'classifyFine' function returns the same integer value as // 'classify'. For negative numbers, 'classifyFine' returns an integer // value equal to value returned by 'classify' bit-wise OR'ed with // 'k_NEGATIVE'. For signaling NaNs, 'classifyFine' returns 'k_SNAN', // which has the integer value of 'k_NAN | k_SIGNALING'. static bool isZero(float number); static bool isZero(double number); // Return 'true' if the specified floating point 'number' has a value // of positive or negative zero, and 'false' otherwise. static bool isNormal(float number); static bool isNormal(double number); // Return 'true' if the specified floating point 'number' holds a // normal value (neither zero, subnormal, infinite, nor NaN). This // function is equivalent to the 'isnormal' macro in C99. static bool isSubnormal(float number); static bool isSubnormal(double number); // Return 'true' if the specified floating point 'number' holds a // subnormal value, and 'false' otherwise. static bool isInfinite(float number); static bool isInfinite(double number); // Return 'true' if the specified floating point 'number' has a value // of positive or negative infinity, and 'false' otherwise. This // function is equivalent to the 'isinf' macro in C99. Note that // infinity is a valid floating-point value and is not a "NaN". static bool isNan(float number); static bool isNan(double number); // Return 'true' if the specified floating point 'number' has a value // that does not represent a real number ("not-a-number" or "NaN"), and // 'false' otherwise. This function is equivalent to the 'isnan' macro // in C99. Note that if this method returns 'true', then either // 'isQuietNan' or 'isSignalingNan' will also return 'true'. static bool signBit(float number); static bool signBit(double number); // Return 'true' if the specified floating point 'number' has its sign // bit set (i.e., it is negative), and 'false' otherwise. This // function is equivalent to the 'signbit' macro in C99. Note that // this function will return 'true' for some NaNs, even though the // concepts of negative and positive do not apply to NaNs. static bool isFinite(float number); static bool isFinite(double number); // Return 'true' if the specified floating point 'number' is normal, // subnormal or zero, and 'false' if 'number' is infinite or NaN. This // function is equivalent to the 'isfinite' macro in C99. static bool isQuietNan(float number); static bool isQuietNan(double number); // Return 'true' if the specified floating point 'number' has an // indeterminate value, and 'false' otherwise. An indeterminate // floating-point value ("quiet NaN" or "QNaN") results from an // operation for which the result is not mathematically defined, such // as multiplying infinity by zero. If a QNaN is used in a subsequent // operations the result will also be a QNaN. Note that, because a // QNaN is a NaN, if this method returns 'true', then 'isNan(x)' will // also return 'true'. static bool isSignalingNan(float number); static bool isSignalingNan(double number); // Return 'true' if the specified floating point 'number' has an // invalid value, and 'false' otherwise. An invalid floating-point // value ("signaling NaN" or "SNaN")is never the result of a valid // operation -- it must be produced deliberately (i.e., by calling // 'bsl::numeric_limits<float>::signaling_NaN()'). If an SNaN is used // in a subsequent operation, the result is undefined and may result in // a hardware trap leading to a signal. Some platforms do not support // signaling NaNs, especially for single-precision floats, and will // convert an SNaN to a QNaN on assignment or copy-initialization // (including argument passing). 'isSignalingNan' will always return // 'false' on such platforms. Note that, because an SNaN is a NaN, if // this method returns 'true', then 'isNan(x)' will also return 'true'. }; } // close package namespace } // close enterprise namespace // ============================================================================ // INLINE FUNCTION DEFINITIONS // ============================================================================ #endif // ---------------------------------------------------------------------------- // Copyright 2015 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------