How XL Fortran Rounds Floating-Point Calculations

Understanding rounding operations in XL Fortran can help you get predictable, consistent results. It can also help you make informed decisions when you have to make tradeoffs between speed and accuracy.

In general, floating-point results from XL Fortran programs are more accurate than those from other implementations because of MAF operations and the higher precision used for intermediate results. If identical results are more important to you than the extra precision and performance of the XL Fortran defaults, read Duplicating the Floating-Point Results of Other Systems.

Selecting the Rounding Mode

To change the rounding mode in a program, you can call the fpsets and fpgets routines, which use an array of logicals named fpstat, defined in the include files /opt/ibmcmp/xlf/9.1/include/fpdt.h and fpdc.h. The fpstat array elements correspond to the bits in the floating-point status and control register.

For floating-point rounding control, the array elements fpstat(fprn1) and fpstat(fprn2) are set as specified in the following table:

Table 15. Rounding-Mode Bits to Use with fpsets and fpgets

fpstat(fprn1) fpstat(fprn2) Rounding Mode Enabled
.true. .true. Round towards -infinity.
.true. .false. Round towards +infinity.
.false. .true. Round towards zero.
.false. .false. Round to nearest.

For example:

      program fptest
        include 'fpdc.h'
 
        call fpgets( fpstat ) ! Get current register values.
        if ( (fpstat(fprn1) .eqv. .false.) .and. +
               (fpstat(fprn2) .eqv. .false.)) then
        print *, 'Before test: Rounding mode is towards nearest'
        print *, '             2.0 / 3.0 = ', 2.0 / 3.0
        print *, '            -2.0 / 3.0 = ', -2.0 / 3.0
        end if
 
        call fpgets( fpstat )   ! Get current register values.
        fpstat(fprn1) = .TRUE.  ! These 2 lines mean round towards
        fpstat(fprn2) = .FALSE. !   +infinity.
        call fpsets( fpstat )
        r = 2.0 / 3.0
        print *, 'Round towards +infinity:  2.0 / 3.0= ', r
 
        call fpgets( fpstat )   ! Get current register values.
        fpstat(fprn1) = .TRUE.  ! These 2 lines mean round towards
        fpstat(fprn2) = .TRUE.  !   -infinity.
        call fpsets( fpstat )
        r = -2.0 / 3.0
        print *, 'Round towards -infinity: -2.0 / 3.0= ', r
        end
! This block data program unit initializes the fpstat array, and so on.
        block data
        include 'fpdc.h'
        include 'fpdt.h'
      end

XL Fortran also provides several procedures that allow you to control the floating-point status and control register of the processor directly. These procedures are more efficient than the fpsets and fpgets subroutines because they are mapped into inlined machine instructions that manipulate the floating-point status and control register (fpscr) directly.

XL Fortran supplies the get_round_mode() and set_round_mode() procedures in the xlf_fp_util module. These procedures return and set the current floating-point rounding mode, respectively.

For example:

      program fptest
        use, intrinsic :: xlf_fp_util
        integer(fpscr_kind) old_fpscr
        if ( get_round_mode() == fp_rnd_rn ) then
        print *, 'Before test: Rounding mode is towards nearest'
        print *, '             2.0 / 3.0 = ', 2.0 / 3.0
        print *, '            -2.0 / 3.0 = ', -2.0 / 3.0
        end if
 
        old_fpscr = set_round_mode( fp_rnd_rp )
        r = 2.0 / 3.0
        print *, 'Round towards +infinity:  2.0 / 3.0 = ', r
 
        old_fpscr = set_round_mode( fp_rnd_rm )
        r = -2.0 / 3.0
        print *, 'Round towards -infinity: -2.0 / 3.0 = ', r
      end
 

XL Fortran supplies the ieee_get_rounding_mode() and ieee_set_rounding_mode() procedures in the ieee_arithmetic module. These portable procedures retrieve and set the current floating-point rounding mode, respectively.

For example:

      program fptest
        use, intrinsic :: ieee_arithmetic
        type(ieee_round_type) current_mode
        call ieee_get_rounding_mode( current_mode )
        if ( current_mode == ieee_nearest ) then
        print *, 'Before test: Rounding mode is towards nearest'
        print *, '             2.0 / 3.0 = ', 2.0 / 3.0
        print *, '            -2.0 / 3.0 = ', -2.0 / 3.0
        end if
 
        call ieee_set_rounding_mode( ieee_up )
        r = 2.0 / 3.0
        print *, 'Round towards +infinity:  2.0 / 3.0 = ', r
 
        call ieee_set_rounding_mode( ieee_down )
        r = -2.0 / 3.0
        print *, 'Round towards -infinity: -2.0 / 3.0 = ', r
      end

Notes:

  1. Extended-precision floating-point values must only be used in round-to-nearest mode.
  2. For thread-safety and reentrancy, the include file /opt/ibmcmp/xlf/9.1/include/fpdc.h contains a THREADLOCAL directive that is protected by the trigger constant IBMT. The invocation commands xlf_r, xlf90_r, and xlf95_r turn on the -qthreaded compiler option by default, which in turn implies the trigger constant IBMT. If you are including the file /opt/ibmcmp/xlf/9.1/include/fpdc.h in code that is not intended to be thread-safe, do not specify IBMT as a trigger constant.

Minimizing Rounding Errors

There are several strategies for handling rounding errors and other unexpected, slight differences in calculated results. You may want to consider one or more of the following strategies:

Minimizing Overall Rounding

Rounding operations, especially in loops, reduce code performance and may have a negative effect on the precision of computations. Consider using double-precision variables instead of single-precision variables when you store the temporary results of double-precision calculations, and delay rounding operations until the final result is computed.

Delaying Rounding until Run Time

The compiler evaluates floating-point expressions during compilation when it can, so that the resulting program does not run more slowly due to unnecessary run-time calculations. However, the results of the compiler's evaluation might not match exactly the results of the run-time calculation. To delay these calculations until run time, specify the nofold suboption of the -qfloat option.

The results may still not be identical; for example, calculations in DATA and PARAMETER statements are still performed at compile time.

The differences in results due to fold or nofold are greatest for programs that perform extended-precision calculations or are compiled with the -O option or both.

Ensuring that the Rounding Mode is Consistent

You can change the rounding mode from its default setting of round-to-nearest. (See for examples.) If you do so, you must be careful that all rounding operations for the program use the same mode:

For example, you might compile a program like the one in Selecting the Rounding Mode with this command if the program consistently uses round-to-plus-infinity mode:

xlf95 -qieee=plus -qfloat=rrm changes_rounding_mode.f
IBM Copyright 2003