Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bdlb_float
[Package bdlb]

Provide floating-point classification types and functions. More...

Namespaces

namespace  bdlb

Detailed Description

Outline
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.