Handling floating-point constant folding and rounding

By default, the compiler replaces most operations involving constant operands with their result at compile time. This process is known as constant folding. Additional folding opportunities may occur with optimization or with the -qnostrict option. The result of a floating-point operation folded at compile time normally produces the same result as that obtained at execution time, except in the following cases:

Related information

Matching compile-time and runtime rounding modes

The default rounding mode used at compile time and run time is round-to-nearest. If your program changes the rounding mode at run time, the results of a floating-point calculation might be slightly different from those that are obtained at compile time. The following example illustrates this:1

#include <float.h>
#include <fenv.h>
#include <stdio.h>

int main ( )
{
	volatile double one = 1.f, three = 3.f;  /* volatiles are not folded */
	double one_third;

	one_third = 1. / 3.;  /* folded */
	printf ("1/3 with compile-time rounding = %.17f\n", one_third);

	fesetround (FE_TOWARDZERO);
	one_third = one / three;  /* not folded */
	fesetround (FE_TONEAREST);2
	printf ("1/3 with execution-time rounding to zero = %.17f\n", one_third);

	fesetround (FE_TONEAREST);
	one_third = one / three;  /* not folded */
	fesetround (FE_TONEAREST);2
	printf ("1/3 with execution-time rounding to nearest = %.17f\n", one_third);

	fesetround (FE_UPWARD);
	one_third = one / three;  /* not folded */
	fesetround (FE_TONEAREST);2
	printf ("1/3 with execution-time rounding to +infinity = %.17f\n", one_third);

	fesetround (FE_DOWNWARD);
	one_third = one / three;  /* not folded */
	fesetround (FE_TONEAREST);2
	printf ("1/3 with execution-time rounding to -infinity = %.17f\n", one_third);
	
	return 0;
 }
Notes:
  1. On AIX, this example must be linked with the system math library, libm, to obtain the functions and macros declared in the fenv.h header file. On AIX 5.1, you will need to use the system functions and macros defined in the header file float.h instead of those used in the example.
  2. See Rounding modes and standard library functions for an explanation of the resetting of the round mode before the call to printf.

When compiled with the default options, this code produces the following results:

1/3 with compile-time rounding = 0.33333333333333331
1/3 with execution-time rounding to zero = 0.33333333333333331
1/3 with execution-time rounding to nearest   = 0.33333333333333331
1/3 with execution-time rounding to +infinity = 0.33333333333333337
1/3 with execution-time rounding to -infinity = 0.33333333333333331 

Because the fourth computation changes the rounding mode to round-to-infinity, the results are slightly different from the first computation, which is performed at compile time, using round-to-nearest. If you do not use the -qfloat=nofold option to suppress all compile-time folding of floating-point computations, it is recommended that you use the -y compiler option with the appropriate suboption to match compile-time and runtime rounding modes. In the previous example, compiling with -yp (round-to-infinity) produces the following result for the first computation:

1/3 with compile-time rounding = 0.33333333333333337

In general, if the rounding mode is changed to +infinity or -infinity, it is recommended that you also use the -qfloat=rrm option.

Rounding modes and standard library functions

On AIX, C and C++ input/output and conversion functions apply the rounding mode in effect to the values that are input or output by the function. These functions include printf, scanf, atof, and ftoa, as well as the C++ input and output operators (>> and <<) on objects like cin and cout.

For example, if the current rounding mode is round-to-infinity, the printf function will apply that rounding mode to the floating-point digit string value it prints, in addition to the rounding that was already performed on a calculation. The following example illustrates this:

#include <float.h>
#include <fenv.h>
#include <stdio.h>

int main( ) 	
{ 	
	volatile double one = 1.f, three = 3.f;  /* volatiles are not folded*/   
	double one_third;    
          
	fesetround (FE_UPWARD);   
	one_third = one / three;  /* not folded */   
	printf ("1/3 with execution-time rounding to +infinity = %.17f\n", one_third); 

	fesetround (FE_UPWARD);   
	one_third = one / three;  /* not folded */  
	fesetround (FE_TONEAREST); 
	printf ("1/3 with execution-time rounding to +infinity = %.17f\n", one_third); 

	return 0;   
}

When compiled with the default options, this code produces the following results:

1/3 with execution-time rounding to +infinity = 0.33333333333333338
1/3 with execution-time rounding to -infinity = 0.33333333333333337 

In the first calculation, the value returned is rounded upward to 0.33333333333333337, but the printf function rounds this value upward again, to print out 0.33333333333333338. The solution to this problem, which is used in the second calculation, is to reset the rounding mode to round-to-nearest just before the call to the library function is made.