There is the following code in which the same expression is evaluated in different rounding modes:
#include <iostream>
#include <fenv.h>
#pragma STDC FENV_ACCESS ON
#define SIZE 8
double foo(double * a, double * b){
double sum = 0.0;
for(unsigned int i = 0; i < SIZE; i++) {
sum+= b[i] / a[i];
}
return sum;
}
int main() {
double a[]={127, 131, 137, 139, 149, 151, 157, 163};
double b[SIZE];
for(unsigned int i = 0; i < SIZE; i++){
b[i] = i+1;
}
printf("to nearest: %.18f \n", foo(a, b));
fesetround(FE_TOWARDZERO);
printf("toward zero: %.18f \n", foo(a, b));
fesetround(FE_UPWARD);
printf("to +infinity: %.18f \n", foo(a, b));
fesetround(FE_DOWNWARD);
printf("to -infinity: %.18f \n", foo(a, b));
return 0;
}
When it is compiled with g++ with the -O0
option, the output is as follows:
to nearest: 0.240773868136782450
toward zero: 0.240773868136782420
to +infinity: 0.240773868136782560
to -infinity: 0.240773868136782420
But when compiling it with the -O3
option, we have:
to nearest: 0.240773868136782480
toward zero: 0.240773868136782480
to +infinity: 0.240773868136782480
to -infinity: 0.240773868136782480
Compiler: g++ (MinGW.org GCC-6.3.0-1) 6.3.0
Why don't the rounding modes change? How to fix it?
(if fesetround
is called at each iteration of the for
loop (inside the foo
function), then the results are correct with any compilation flags.)
UPD: I think the problem is that the compiler computes the value of fesetround
at compile type as @haneefmubarak has pointed out in https://stackoverflow.com/a/26319847/2810512. The question is how to prevent it. (only for one command, fesetround
, not for the whole function).
I wrote wrappers for rounding routines with __attribute__ ((noinline))
and call them in the main
function:
void __attribute__ ((noinline)) rounddown(){
fesetround(FE_DOWNWARD);
}
void __attribute__ ((noinline)) roundup(){
fesetround(FE_UPWARD);
}
int main() {
...
roundup();
printf("to +infinity: %.18f \n", foo(a, b));
rounddown();
printf("to -infinity: %.18f \n", foo(a, b));
...
}
But it does not work. Any ideas?
UPD2: A more clear example:
It is easy to see that the exact result:
2/3 + 2/5 + 4/7 + 4/11 = 2.0017316017316017316...