C99 - IEEE 754 Floating Point Support

IEEE 754 Floating Point Support

A major feature of C99 is its numerics support, and in particular its support for access to the features of IEEE 754 (also known as IEC 60559) floating point hardware present in the vast majority of modern processors (defined in "Annex F IEC 60559 floating-point arithmetic").

On hardware with IEEE 754 floating point:

  • float is defined as IEEE 754 single precision, double is defined as double precision, and long double is defined as IEEE 754 extended precision or quad precision where available (e.g., Intel 80 bit double extended precision on x86 or x86-64 platforms).
  • arithmetic operations and functions are correctly rounded as defined by IEEE 754.
  • expression evaluation is defined to be performed in one of three well-defined methods, indicating whether floating point variables are first promoted to a more precise format in expressions: FLT_EVAL_METHOD == 2 indicates that all internal intermediate computations are performed by default at high precision (long double) where available (e.g., 80 bit double extended); FLT_EVAL_METHOD == 1 performs all internal intermediate expressions in double precision (unless an operand is long double); FLT_EVAL_METHOD == 0 specifies each operation is evaluated only at the precision of the widest operand of each operator. The intermediate result type for operands of a given precision are summarized in the following table:
FLT_EVAL_METHOD float double long double
0 float double long double
1 double double long double
2 long double long double long double

FLT_EVAL_METHOD == 2 is the safest default as it limits the risk of rounding errors affecting numerically unstable expressions (see IEEE 754 design rationale) and is the designed default method for x87 hardware; FLT_EVAL_METHOD == 1 was the default evaluation method originally used in K&R C, which promoted all floats to double in expressions; and FLT_EVAL_METHOD == 0 is also commonly used and specifies a strict "evaluate to type" of the operands. (For gcc, FLT_EVAL_METHOD == 2 is the default on 32 bit x86, and FLT_EVAL_METHOD == 0 is the default on 64 bit x86-64, but FLT_EVAL_METHOD == 2 can be specified on x86-64 with option -mfpmath=387). Note that prior to the precision of intermediate values being precisely specified in C99, C compilers could round intermediate results inconsistently, especially when using x87 floating point hardware, leading to compiler-specific behaviour; such inconsistencies are not permitted in compilers conforming to C99 (annex F).

The following annotated example C99 code for computing a continued fraction function demonstrates the main features:

// compile with: gcc -std=c99 -mfpmath=387 -o test_c99_fp -lm test_c99_fp.c #include #include #include #include #include #include double compute_fn(double z) { #pragma STDC FENV_ACCESS ON // assert( FLT_EVAL_METHOD == 2 ); // if (isnan(z)) printf("z is not a number\n"); // if (isinf(z)) printf("z is infinite\n"); long double r; // r = 7.0 - 3.0/(z - 2.0 - 1.0/(z - 7.0 + 10.0/(z - 2.0 - 2.0/(z - 3.0)))); // feclearexcept(FE_DIVBYZERO); // bool raised = fetestexcept(FE_OVERFLOW); // if (raised) printf("Unanticipated overflow.\n"); return r; } int main(void) { #ifndef __STDC_IEC_559__ printf("Warning: __STDC_IEC_559__ not defined. IEEE 754 floating point not fully supported.\n") // #endif #pragma STDC FENV_ACCESS ON #ifdef TEST_NUMERIC_STABILITY_UP fesetround(FE_UPWARD); // #elif TEST_NUMERIC_STABILITY_DOWN fesetround(FE_DOWNWARD); #endif printf("%.7g\n", compute_fn(3.0)); printf("%.7g\n", compute_fn(NAN)); return 0; }

Footnotes:

  1. As the IEEE 754 status flags are manipulated in this function, this #pragma is needed to avoid the compiler incorrectly rearranging such tests when optimising.
  2. C99 defines a limited number of expression evaluation methods: the current compilation mode can be checked to ensure it meets the assumptions the code was written under.
  3. The special values such as NaN and positive or negative infinity can be tested and set.
  4. long double is defined as IEEE 754 double extended or quad precision if available. Using higher precision than required for intermediate computations can minimize round-off error (the typedef double_t can be used for code that is portable under all FLT_EVAL_METHODs).
  5. The main function to be evaluated. Although it appears that some arguments to this continued fraction, e.g., 3.0, would lead to a divide-by-zero error, in fact the function is well-defined at 3.0 and division by 0 will simply return a +infinity that will then correctly lead to a finite result: IEEE 754 is defined not to trap on such exceptions by default and is designed so that they can very often be ignored, as in this case. (Note that if FLOAT_EVAL_METHOD is defined as 2 then all internal computations including constants will be performed in long double precision; if FLT_EVAL_METHOD is defined as 0 then additional care is need to ensure this, including possibly additional casts and explicit specification of constants as long double).
  6. As the raised divide-by-zero flag is not an error in this case, it can simply be dismissed to clear the flag for use by later code.
  7. In some cases other, exceptions may be an error, such as overflow (although it can in fact be shown that this cannot occur in this case).
  8. __STDC_IEC_559__ is defined if "Annex F IEC 60559 floating-point arithmetic" is fully implemented by the compiler.
  9. The default rounding mode is round to even for IEEE 754, but explicitly setting the rounding mode toward + and - infinity (by defining TEST_NUMERIC_STABILITY_UP etc. in this example, when debugging) can be used to diagnose numerical instability. Note that this method can be used even if compute_fn is part of a separately compiled binary library.

Read more about this topic:  C99

Famous quotes containing the words floating, point and/or support:

    People who treat other people as less than human must not be surprised when the bread they have cast on the waters comes floating back to them, poisoned.
    James Baldwin (1924–1987)

    “Circumstantial evidence is a very tricky thing,” answered Holmes thoughtfully. “It may seem to point very straight to one thing, but if you shift your own point of view a little, you may find it pointing in an equally uncompromising manner to something entirely different.”
    Sir Arthur Conan Doyle (1859–1930)

    Many people now believe that if fathers are more involved in raising children than they were, children and sons in particular will learn that men can be warm and supportive of others as well as be high achievers. Thus, fathers’ involvement may be beneficial not because it will help support traditional male roles, but because it will help break them down.
    Joseph H. Pleck (20th century)