Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Errors during floating-point operations are often neglected by programmers who instead focus on validating operands before an operation. Errors occurring during floating-point operations are admittedly difficult to determine and diagnose, but the benefits of doing so often outweighs outweigh the costs. This recommendation suggests ways to capture errors during floating-point operations.

The following code exhibits undefined behavior:

Code Block

int j = 0;
int iResult = 1 / j;

On most implementations, integer division by zero is a terminal error, commonly printing a diagnostic message and aborting the program.

Code Block

double x = 0.0;
double dResult = 1 / x;

Floating-point division by zero also results in undefined behavior, although most implementations do not treat this treat it as a terminal error. If additional precautions are not taken, this  it results in a silent error.

The most portable way of determining if a floating-point exceptional condition has occurred is to use the floating-point exception facilities provided by C99 C in fenv.h [ISO/IEC 9899:19992011].

However, the C99 C floating-point exception functions are not without problems. The following caveats exist regarding the interaction between floating-point exceptions and conversions:

  • Conversion from floating-point to integer may cause an "invalid" floating-point exception. If this occurs, the value of that integer is undefined and should not be used.
  • Most implementations fail to raise "invalid" for conversions from any negative or "large" positive floating-point values to unsigned integer types or to signed char. (See tflt2int.c.)
  • When a noninteger floating-point value is converted to an integer the "inexact" floating-point exception is raised.

For information regarding floating-point number conversions, see rule FLP34-C. Ensure that floating point conversions are within range of the new type.

C99 does The C standard does not require all implementations to support floating-point exceptions. Each exception macro in fenv.h is defined if, and only if, the corresponding exception is supported. Only implementations that use IEC 60559 (formerly IEEE-754) floating-point arithmetic are required to support all five exceptions defined by C99C (see Section 7.6.2 [ISO/IEC 9899:2011]). Nevertheless, these functions are the most portable solution for handling floating-point exceptions.

A less portable but potentially more secure solution is to use the capabilities provided by the underlying implementation. If this approach is taken, the caveats of that system need to be well understood. Table 6-1 can serve as  The following table provides a starting point for some common operating systems:

Operating System

How to handle floating point errors Handle Floating-Point Errors

Linux
Solaris 10
AIX 5.3
HP-UX 11.31
Mac OS X 10.5

Use the C99 C floating-point exception functions.

Windows

Either use Use either the C99 C floating-point exception function functions or structured exception handling through _fpieee_flt [MSDN].

Noncompliant Code Example

...

Code Block
bgColor#FFCCCC
langc

void fpOper_noErrorChecking(void) {
  /* ... */
  double a = 1e-40, b, c = 0.1;
  float x = 0, y;
  /* inexact and underflows */
  y = a;
  /* divide by zero operation */
  b = y / x;
  /* inexact (loss of precision) */
  c = sin(30) * a;
  /* ... */
}

However, exceptional conditions (as indicated by the comments) occur that may lead to unexpected arithmetic results.

Compliant Solution (

...

C11)

This compliant solution uses C99 C standard functions to handle floating-point errors.

Code Block
bgColor#ccccff
langc

#include <fenv.h>
#pragma STDC FENV_ACCESS ON

void fpOper_fenv(void) {
  double a = 1e-40, b, c = 0.1;
  float x = 0, y;
  int fpeRaised;
  /* ... */

  feclearexcept(FE_ALL_EXCEPT);
  /* Store a into y is inexact and underflows: */
  y = a;
  fpeRaised = fetestexcept(FE_ALL_EXCEPT);
  /* fpeRaised  has FE_INEXACT and FE_UNDERFLOW */

  feclearexcept(FE_ALL_EXCEPT);

  /* divide by zero operation */
  b = y / x;
  fpeRaised = fetestexcept(FE_ALL_EXCEPT);
  /* fpeRaised has FE_DIVBYZERO */

  feclearexcept(FE_ALL_EXCEPT);

  c = sin(30) * a;
  fpeRaised = fetestexcept(FE_ALL_EXCEPT);
  /* fpeRaised has FE_INEXACT */

  feclearexcept(FE_ALL_EXCEPT);
  /* ... */
}

...

Microsoft Visual Studio 2008 and earlier versions do not support C99 C functions to handle floating-point errors. Windows provides an alternative method for handling floating-point errors using _statusfp(), _statusfp2(), and _clearfp().

Code Block
bgColor#ccccff
langc

void fpOper_usingStatus(void) {
  /* ... */
  double a = 1e-40, b, c;
  float x = 0, y;
  unsigned int rv = _clearfp();

  /* Store into y is inexact and underflows: */
  y = a;
  rv = _clearfp();  /* rv has _SW_INEXACT and _SW_UNDERFLOW */

  /* zero-divide */
  b = y / x; rv = _clearfp(); /* rv has _SW_ZERODIVIDE */

  /* inexact */
  c = sin(30) * a; rv = _clearfp(); /* rv has _SW_INEXACT */
  /* ... */
}

...

Code Block
bgColor#ccccff
langc

void fp_usingSEH(void) {
  /* ... */
  double a = 1e-40, b, c = 0.1;
  float x = 0, y;
  unsigned int rv ;

  unmask_fpsr();

  _try {
    /* Store into y is inexact and underflows */
    y = a;

    /* divide by zero operation */
    b = y / x;

    /* inexact */
    c = sin(30) * a;
  }

  _except (_fpieee_flt(
             GetExceptionCode(),
             GetExceptionInformation(),
             fpieee_handler)) {
  {
  printf ("fpieee_handler: EXCEPTION_EXECUTE_HANDLER");
  }

  /* ... */
}

void unmask_fpsr(void) {
  unsigned int u;
  unsigned int control_word;
  _controlfp_s(&control_word, 0, 0);
  u = control_word & ~(_EM_INVALID
                     | _EM_DENORMAL
                     | _EM_ZERODIVIDE
                     | _EM_OVERFLOW
                     | _EM_UNDERFLOW
                     | _EM_INEXACT);
  _controlfp_s( &control_word, u, _MCW_EM);
  return ;
}

int fpieee_handler(_FPIEEE_RECORD *ieee) {
  /* ... */

  switch (ieee->RoundingMode) {
    case _FpRoundNearest:
      /* ... */
      break;

      /* Other RMs include _FpRoundMinusInfinity,
       * _FpRoundPlusInfinity, _FpRoundChopped */

      /* ... */
    }

  switch (ieee->Precision) {
    case _FpPrecision24:
      /* ... */
      break;

      /* Other Ps include _FpPrecision53*/
      /* ... */
    }

   switch (ieee->Operation) {
     case _FpCodeAdd:
       /* ... */
       break;

       /* Other Ops include _FpCodeSubtract, _FpCodeMultiply,
        * _FpCodeDivide, _FpCodeSquareRoot, _FpCodeCompare,
        * _FpCodeConvert, _FpCodeConvertTrunc */
       /* ... */
    }

  /* 
   * process the bitmap ieee->Cause.
   * process the bitmap ieee->Enable.
   * process the bitmap ieee->Status.
   * process the Operand ieee->Operand1, 
   * evaluate format and Value.
   * process the Operand ieee->Operand2, 
   * evaluate format and Value.
   * process the Result ieee->Result, 
   * evaluate format and Value .
   * The result should be set according to the operation 
   * specified in ieee->Cause and the result format as 
   * specified in ieee->Result.
   */

  /* ... */
}

...

sectioncould

Could detect violations of this rule

,

by ensuring that floating-point operations are surrounded by feclearexcept() and fetestexcept(). It would need to look for type conversions to float or double, divisions (by a number not known to be nonzero), and multiplication. It may be wisest to apply this to all floating-point operations in general.

Tool

Version

Checker

Description

Compass/ROSE

 

 

Section

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this recommendation on the CERT website.

...

MITRE CWE: CWE-369, "Divide By Zeroby zero"

Bibliography

[IEEE 754]
[Intel 2001]
[Keil 2008]
[MSDN] "fpieee_flt (CRT)"
[Open Group 2004] "fenv.h - Floating-point environment"
[SecurityFocus 2007]

...