如何使用优化级别防止C99浮点代码更改结果

如何使用优化级别防止C99浮点代码更改结果,c,gcc,optimization,c99,ieee,C,Gcc,Optimization,C99,Ieee,我在下面编写此代码是为了测试系统是否符合C99的IEEE 754。问题是结果会随着优化级别的不同而变化。如果我坚持使用优化级别0,那么大多数测试都会通过。但是如果我将优化级别增加到-O3,那么大多数测试都会失败。我在一家商店里试过。它没有改变任何结果 我已经在Mac OS 10.4.11 PowerPC上使用GCC 5.2.0尝试过这个程序,在Mac OS 10.6.8上使用GCC 4.2.1和GCC 4.9.2尝试过这个程序。结果是一样的。大多数测试只有在使用优化级别0(-O0)时才通过。我认

我在下面编写此代码是为了测试系统是否符合C99的IEEE 754。问题是结果会随着优化级别的不同而变化。如果我坚持使用优化级别0,那么大多数测试都会通过。但是如果我将优化级别增加到-O3,那么大多数测试都会失败。我在一家商店里试过。它没有改变任何结果

我已经在Mac OS 10.4.11 PowerPC上使用GCC 5.2.0尝试过这个程序,在Mac OS 10.6.8上使用GCC 4.2.1和GCC 4.9.2尝试过这个程序。结果是一样的。大多数测试只有在使用优化级别0(-O0)时才通过。我认为这是GCC的一个bug,但在我报告它之前,我想听听其他人对代码的看法

这就是我编译代码的方式:gcc-o testc99 main.c-O0

// FILE: main.c
// Description: Tests C99 IEEE 754 compliance.
// Note: use -O0 when compiling to make most of the tests pass
// Date: 11-24-2016

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

#pragma STDC FENV_ACCESS ON

// Used to convert unsigned integer <--> double 
union Converter
{
    double d;
    uint64_t i;
};

typedef union Converter Converter;

#pragma STDC FENV_ACCESS on

int check_for_exceptions(void)
{
    // Set to 1 to enable debug printing
    if (0) {
        if (fetestexcept(FE_DIVBYZERO)) {
            printf("FE_DIVBYZERO detected\n");
        }
        if (fetestexcept(FE_INEXACT)) {
            printf("FE_INEXACT detected\n");
        }
        if (fetestexcept(FE_INVALID)) {
            printf("FE_INVALID detected\n");
        }
        if (fetestexcept(FE_OVERFLOW)) {
            printf("FE_OVERFLOW detected\n");
        }
        if (fetestexcept(FE_UNDERFLOW)) {
            printf("FE_UNDERFLOW detected\n");
        }
    }
    return fetestexcept(FE_ALL_EXCEPT);
}

// Adds two really big numbers together in order to cause an overflow
void test_overflow(void)
{
    double answer, num1, num2;
    num1 = 1.7 * pow(10, 308);  // the very limits of IEEE 754 double precision numbers
    num2 = num1;
    feclearexcept(FE_ALL_EXCEPT);
    // adding two really big numbers together should trigger an overflow
    answer = num1 + num2;
    printf("Test overflow...");
    if (check_for_exceptions() == (FE_OVERFLOW | FE_INEXACT)) {
        printf("pass\n");
    } else {
        printf("fail\n");
    }
}

void test_underflow(void)
{
    double answer;
    feclearexcept(FE_ALL_EXCEPT);
    //answer = DBL_MIN / 3000000000000000.0;    // does not produce an exception
    answer = fma(DBL_MIN, 1.0/10.0, 0);     // Inexact and underflow exceptions produced
    printf("Test underflow...");
    if (check_for_exceptions() == (FE_UNDERFLOW | FE_INEXACT)) {
        printf("pass\n");
    } else {
        printf("fail\n");
    }
}

// Test if the inexact exception can be produced under the right conditions
void test_inexact(void)
{
    double answer;
    feclearexcept(FE_ALL_EXCEPT);
    answer = log(1.1);
    printf("Test inexact...");
    if (check_for_exceptions() == FE_INEXACT) {
        printf("pass\n");
    } else {
        printf("fail\n");
    }
}

// test to see if the invalid exception works
void test_invalid(void)
{
    double d;
    feclearexcept(FE_ALL_EXCEPT);
    d = sqrt(-1.0);
    printf("Test invalid...");
    if (check_for_exceptions() == FE_INVALID) {
        printf("pass\n");
    } else {
        printf("fail\n");
    }
}

// test the fused multiply-add operation
void test_fma(void)
{
    double result, correct_answer, num1, num2, num3;
    int iteration, max_iterations;

    result = 0.0;
    correct_answer = -13819435189200605973481192570224640.0;
    num1 = 5.2;
    num2 = 1.3 * pow(10, 18);
    num3 = -3.0;
    max_iterations = pow(10, 7);

    feclearexcept(FE_ALL_EXCEPT);

    // Test large number of multiplication, addition, and subtraction calculations
    for (iteration = 0; iteration < max_iterations; iteration++) {
        result += fma(num1, num2, num3);
        num1 += 1.0000002;
        num2 -= 1.3044 * pow(10,14);
        num3 += 0.953343;
    }

    // Test division - or multiplication with the reciprical
    result = fma(result, 1.0/3.14159265, -987654321.123456789);

    printf("Test fma()...");
    if (result == correct_answer) {
        printf("pass\n");
    } else {
        printf("fail\n");
    }
}

// Test what happens with infinity - infinity
void test_inf_minus_inf()
{
    double answer;
    //answer = INFINITY - INFINITY;         // does not cause an exception, but does make a nan
    feclearexcept(FE_ALL_EXCEPT);
    answer = fma(INFINITY, 1, -INFINITY);   // does cause an invalid exception, answer is nan. -INFINITY - INFINITY doesn't cause an exception
    printf("Testing infinity - infinity...");
    if (check_for_exceptions() == FE_INVALID) {
        printf("pass\n");
    } else {
        printf("fail\n");
    }
}

// Test signalling nan - should produce an invalid exception
void test_snan()
{
    double result;
    Converter c;
    c.i = 0x7FF0000000000001; 
    feclearexcept(FE_ALL_EXCEPT);
    result = fma(c.d, 10.4, 0.11);
    printf("Test snan...");
    if (check_for_exceptions() == FE_INVALID) {
        printf("pass\n");
    } else {
        printf("fail\n");
    }
}

// Test quiet nan - no exceptions should be produced
void test_qnan()
{
    Converter c;
    double result;    
    c.i = 0x7fffffff;
    feclearexcept(FE_ALL_EXCEPT);
    result = fma(c.d, 10.4, 0.11);
    printf("Test qnan...");
    if (check_for_exceptions() == 0) {
        printf("pass\n");
    } else {
        printf("fail\n");
    }
}

// Test infinity * zero for invalid exception
void test_inf_times_zero()
{
    double answer;
    //answer = INFINITY * 0;   // answer is nan, no exception
    feclearexcept(FE_ALL_EXCEPT);
    answer = fma(INFINITY, 0, 0); // answer is nan, invalid exception raised
    printf("Test infinity * 0...");
    if (check_for_exceptions() == FE_INVALID) {
        printf("pass\n");
    } else {
        printf("fail\n");
    }
}

// Test division by zero exception
void test_one_divided_by_zero()
{
    double answer;
    feclearexcept(FE_ALL_EXCEPT);
    answer = fma(1.0, 1.0/0.0, 0.0);       // division by zero exception, answer = inf
    //answer = 1.0/0.0;                     // division by zero exception, answer = inf
    printf("Test division by zero...");
    if (check_for_exceptions() == FE_DIVBYZERO) {
        printf("pass\n");
    } else {
        printf("fail\n");
    }
}

// verify all rounding modes work correctly
void test_rounding_modes(void)
{
    double result, expected_result;

    printf("Test rounding...");
    do {
        fesetround(FE_TONEAREST);
        expected_result = 2.0;
        result = rint(2.1);        
        if (result != expected_result) {
            break;
        }

        fesetround(FE_UPWARD);
        expected_result = 3.0;
        result = rint(2.1);
        if (result != expected_result) {
            break;
        }

        fesetround(FE_DOWNWARD);
        expected_result = 2.0;
        result = rint(2.1);
        if (result != expected_result) {
            break;
        }

        fesetround(FE_TOWARDZERO);
        expected_result = 2.0;
        result = rint(2.1);
        if (result != expected_result) {
            break;
        }

        printf("pass\n");
        return;
    } while (0);
    printf("fail\n");
}

// Test square root results
void test_sqrt(void)
{
    double answer, result, base, expected_result;
    base = 8.123456;
    answer = pow(base, 2);
    result = sqrt(answer);
    expected_result = 8.1234559999999973;
    printf("Test sqrt...");
    if (result == expected_result) {
        printf("pass\n");
    } else {
        printf("fail\n");
    }
}

int main (int argc, const char * argv[]) {
    test_inf_minus_inf();
    test_inf_times_zero();
    test_one_divided_by_zero();
    test_fma();
    test_overflow();
    test_underflow();
    test_inexact();
    test_invalid();
    test_snan();
    test_qnan();
    test_rounding_modes();
    test_sqrt();
    return 0;
}
//文件:main.c
//描述:测试C99与IEEE 754的符合性。
//注意:编译时使用-O0使大多数测试通过
//日期:2016年11月24日
#包括
#包括
#包括
#包括
#包括
#布拉格STDC FENV_通道
//用于转换无符号整数双精度
联合变流器
{
双d;
uint64_t i;
};
DEF型活接头转换器;
#布拉格STDC FENV_通道
int check_是否存在异常(无效)
{
//设置为1以启用调试打印
如果(0){
if(fetestexcept(feu除以零)){
printf(“检测到FE_DIVBYZERO\n”);
}
if(异常(FE_不精确)){
printf(“检测到FE_不精确\n”);
}
如果(fetestexcept(FE_无效)){
printf(“检测到FE_无效\n”);
}
if(fetestexcept(FE_溢出)){
printf(“检测到FE_溢出\n”);
}
if(fetestexcept(FE_下溢)){
printf(“检测到FE_下溢\n”);
}
}
返回fetestexcept(FE_所有_除外);
}
//将两个非常大的数字相加以导致溢出
无效测试_溢出(无效)
{
双答案,num1,num2;
num1=1.7*pow(10308);//IEEE 754双精度数字的极限
num2=num1;
FeClearException(FE_ALL_除外);
//将两个非常大的数字相加应该会触发溢出
答案=num1+num2;
printf(“测试溢出…”);
如果(检查是否存在异常()=(FE_溢出| FE_不精确)){
printf(“pass\n”);
}否则{
printf(“失败\n”);
}
}
孔隙测试\u底流(孔隙)
{
双重回答;
FeClearException(FE_ALL_除外);
//answer=DBL_MIN/30000000000000000.0;//不产生异常
答案=fma(DBL_MIN,1.0/10.0,0);//产生不精确和下溢异常
printf(“测试下溢…”);
如果(检查是否存在异常()=(FE_下溢| FE_不精确)){
printf(“pass\n”);
}否则{
printf(“失败\n”);
}
}
//测试在正确的条件下是否可以产生不精确异常
无效测试不精确(无效)
{
双重回答;
FeClearException(FE_ALL_除外);
答案=对数(1.1);
printf(“测试不精确…”);
如果(检查是否存在异常()==FE\U不精确){
printf(“pass\n”);
}否则{
printf(“失败\n”);
}
}
//测试以查看无效异常是否有效
无效测试_无效(无效)
{
双d;
FeClearException(FE_ALL_除外);
d=sqrt(-1.0);
printf(“测试无效…”);
如果(检查是否存在异常()==FE\U无效){
printf(“pass\n”);
}否则{
printf(“失败\n”);
}
}
//测试融合乘法加法操作
失效测试(失效)
{
双重结果,正确答案,num1,num2,num3;
int迭代,max_迭代;
结果=0.0;
正确答案=-13819435189200605973481192570224640.0;
num1=5.2;
num2=1.3*pow(10,18);
num3=-3.0;
最大迭代次数=功率(10,7);
FeClearException(FE_ALL_除外);
//测试大量乘法、加法和减法计算
对于(迭代=0;迭代