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.
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/10.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:
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
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:
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.
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.
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